From 46e2493cf528b4ee5f30c53b1ca47641105b0895 Mon Sep 17 00:00:00 2001 From: greenart7c3 Date: Wed, 30 Oct 2024 16:39:42 -0300 Subject: [PATCH] Remove push notifications server --- app/build.gradle | 11 +- app/google-services.json | 48 ---- app/src/free/AndroidManifest.xml | 13 - .../service/PushDistributorHandler.kt | 121 ---------- .../service/PushMessageReceiver.kt | 130 ---------- .../service/PushNotificationUtils.kt | 114 --------- .../ui/SelectNotificationProvider.kt | 82 ------- .../nostrsigner/LocalPreferences.kt | 3 - .../greenart7c3/nostrsigner/NostrSigner.kt | 21 +- .../nostrsigner/database/ApplicationDao.kt | 3 + .../nostrsigner/models/AmberSettings.kt | 2 - .../service/ConnectivityService.kt | 17 +- .../service/EventNotificationConsumer.kt | 58 ----- .../nostrsigner/service/IntentUtils.kt | 5 - .../service/NotificationDataSource.kt | 5 +- .../nostrsigner/service/RegisterAccounts.kt | 228 ------------------ .../nostrsigner/ui/AccountScreen.kt | 157 ++++-------- .../nostrsigner/ui/EditConfigurationScreen.kt | 17 +- .../greenart7c3/nostrsigner/ui/MainScreen.kt | 41 +--- .../nostrsigner/ui/NotificationTypeScreen.kt | 133 ---------- .../nostrsigner/ui/PermissionsScreen.kt | 38 +-- .../nostrsigner/ui/SettingsScreen.kt | 117 +++------ .../ui/actions/EditRelaysDialog.kt | 7 +- .../ui/components/MultiEventHomeScreen.kt | 3 +- .../ui/components/SingleEventHomeScreen.kt | 13 +- .../nostrsigner/ui/navigation/Route.kt | 8 - app/src/play/AndroidManifest.xml | 73 ------ .../nostrsigner/service/Notifications.kt | 71 ------ .../service/PushNotificationUtils.kt | 50 ---- .../ui/SelectNotificationProvider.kt | 6 - build.gradle | 1 - build.sh | 8 +- gradle/libs.versions.toml | 7 - 33 files changed, 129 insertions(+), 1482 deletions(-) delete mode 100644 app/google-services.json delete mode 100644 app/src/free/java/com/greenart7c3/nostrsigner/service/PushDistributorHandler.kt delete mode 100644 app/src/free/java/com/greenart7c3/nostrsigner/service/PushMessageReceiver.kt delete mode 100644 app/src/free/java/com/greenart7c3/nostrsigner/service/PushNotificationUtils.kt delete mode 100644 app/src/free/java/com/greenart7c3/nostrsigner/ui/SelectNotificationProvider.kt delete mode 100644 app/src/main/java/com/greenart7c3/nostrsigner/service/RegisterAccounts.kt delete mode 100644 app/src/main/java/com/greenart7c3/nostrsigner/ui/NotificationTypeScreen.kt delete mode 100644 app/src/play/AndroidManifest.xml delete mode 100644 app/src/play/java/com/greenart7c3/nostrsigner/service/Notifications.kt delete mode 100644 app/src/play/java/com/greenart7c3/nostrsigner/service/PushNotificationUtils.kt delete mode 100644 app/src/play/java/com/greenart7c3/nostrsigner/ui/SelectNotificationProvider.kt diff --git a/app/build.gradle b/app/build.gradle index 6a13d285..c0dd3625 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,6 @@ plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.jetbrainsKotlinAndroid) alias(libs.plugins.gradle.ktlint) version(libs.versions.ktlint) - alias(libs.plugins.google.gms) version(libs.versions.gms) alias(libs.plugins.kotlin.ksp) version(libs.versions.ksp) alias(libs.plugins.jetbrainsComposeCompiler) } @@ -58,7 +57,6 @@ android { 'ku-rTR', 'lt-rLT', 'ne-rNP', - 'night', 'nl', 'nl-rBE', 'pcm-rNG', @@ -131,11 +129,8 @@ android { flavorDimensions += "version" productFlavors { - create("play") { - getIsDefault().set(true) - dimension = "version" - } create("free") { + getIsDefault().set(true) dimension = "version" } create("offline") { @@ -241,12 +236,8 @@ dependencies { // Biometrics implementation libs.biometric.ktx - playImplementation platform(libs.firebase.bom) - playImplementation(libs.firebase.messaging) - freeImplementation(libs.unfiedpush) ksp(libs.room.compiler) implementation(libs.room.ktx) freeImplementation(libs.okhttp) - playImplementation(libs.okhttp) } diff --git a/app/google-services.json b/app/google-services.json deleted file mode 100644 index 1afad829..00000000 --- a/app/google-services.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "project_info": { - "project_number": "575711686692", - "project_id": "amber-c362b", - "storage_bucket": "amber-c362b.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:575711686692:android:11451e08365ea86f6ce8b1", - "android_client_info": { - "package_name": "com.greenart7c3.nostrsigner" - } - }, - "oauth_client": [], - "api_key": [ - { - "current_key": "AIzaSyDB2tmjxGmFx_Oz25YhtKkheWbdb_NlnfU" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:575711686692:android:fb6ef62b68245d696ce8b1", - "android_client_info": { - "package_name": "com.greenart7c3.nostrsigner.debug" - } - }, - "oauth_client": [], - "api_key": [ - { - "current_key": "AIzaSyDB2tmjxGmFx_Oz25YhtKkheWbdb_NlnfU" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/app/src/free/AndroidManifest.xml b/app/src/free/AndroidManifest.xml index 7f803246..208539d7 100644 --- a/app/src/free/AndroidManifest.xml +++ b/app/src/free/AndroidManifest.xml @@ -26,19 +26,6 @@ /> - - - - - - - - - - diff --git a/app/src/free/java/com/greenart7c3/nostrsigner/service/PushDistributorHandler.kt b/app/src/free/java/com/greenart7c3/nostrsigner/service/PushDistributorHandler.kt deleted file mode 100644 index 02e09067..00000000 --- a/app/src/free/java/com/greenart7c3/nostrsigner/service/PushDistributorHandler.kt +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (c) 2024 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.greenart7c3.nostrsigner.service - -import android.content.Context -import android.content.pm.PackageManager -import android.os.Build -import android.util.Log -import com.greenart7c3.nostrsigner.LocalPreferences -import com.greenart7c3.nostrsigner.NostrSigner -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.unifiedpush.android.connector.UnifiedPush - -interface PushDistributorActions { - fun getSavedDistributor(): String - - fun getInstalledDistributors(): List - - fun saveDistributor(distributor: String) - - fun removeSavedDistributor() -} - -object PushDistributorHandler : PushDistributorActions { - private val appContext = NostrSigner.getInstance().applicationContext - private val unifiedPush: UnifiedPush = UnifiedPush - - private var endpointInternal = NostrSigner.getInstance().settings.endpoint - - fun getSavedEndpoint() = endpointInternal - - @OptIn(DelicateCoroutinesApi::class) - fun setEndpoint(newEndpoint: String) { - endpointInternal = newEndpoint - GlobalScope.launch(Dispatchers.IO) { - NostrSigner.getInstance().settings = NostrSigner.getInstance().settings.copy(endpoint = newEndpoint) - LocalPreferences.saveSettingsToEncryptedStorage(NostrSigner.getInstance().settings) - } - Log.d("PushHandler", "New endpoint saved : $endpointInternal") - } - - @OptIn(DelicateCoroutinesApi::class) - fun removeEndpoint() { - endpointInternal = "" - GlobalScope.launch(Dispatchers.IO) { - NostrSigner.getInstance().settings = NostrSigner.getInstance().settings.copy(endpoint = "") - LocalPreferences.saveSettingsToEncryptedStorage(NostrSigner.getInstance().settings) - } - } - - override fun getSavedDistributor(): String { - return unifiedPush.getSavedDistributor(appContext) ?: "" - } - - fun savedDistributorExists(): Boolean = getSavedDistributor().isNotEmpty() - - override fun getInstalledDistributors(): List { - return unifiedPush.getDistributors(appContext) - } - - fun formattedDistributorNames(): List { - val distributorsArray = getInstalledDistributors().toTypedArray() - val distributorsNameArray = - distributorsArray - .map { - try { - val ai = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - appContext.packageManager.getApplicationInfo( - it, - PackageManager.ApplicationInfoFlags.of( - PackageManager.GET_META_DATA.toLong(), - ), - ) - } else { - appContext.packageManager.getApplicationInfo(it, 0) - } - appContext.packageManager.getApplicationLabel(ai) - } catch (e: PackageManager.NameNotFoundException) { - it - } - as String - } - .toTypedArray() - return distributorsNameArray.toList() - } - - override fun saveDistributor(distributor: String) { - unifiedPush.saveDistributor(appContext, distributor) - unifiedPush.registerApp(appContext) - } - - override fun removeSavedDistributor() { - unifiedPush.safeRemoveDistributor(appContext) - } - - fun forceRemoveDistributor(context: Context) { - unifiedPush.forceRemoveDistributor(context) - } -} diff --git a/app/src/free/java/com/greenart7c3/nostrsigner/service/PushMessageReceiver.kt b/app/src/free/java/com/greenart7c3/nostrsigner/service/PushMessageReceiver.kt deleted file mode 100644 index c4bdc89d..00000000 --- a/app/src/free/java/com/greenart7c3/nostrsigner/service/PushMessageReceiver.kt +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright (c) 2024 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.greenart7c3.nostrsigner.service - -import android.content.Context -import android.util.Log -import android.util.LruCache -import com.greenart7c3.nostrsigner.LocalPreferences -import com.greenart7c3.nostrsigner.NostrSigner -import com.greenart7c3.nostrsigner.database.LogEntity -import com.vitorpamplona.quartz.events.Event -import com.vitorpamplona.quartz.events.GiftWrapEvent -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel -import kotlinx.coroutines.launch -import org.unifiedpush.android.connector.MessagingReceiver - -class PushMessageReceiver : MessagingReceiver() { - companion object { - private val TAG = "Amber-OSSPushReceiver" - } - - private val appContext = NostrSigner.getInstance().applicationContext - private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - private val eventCache = LruCache(100) - private val pushHandler = PushDistributorHandler - - override fun onMessage( - context: Context, - message: ByteArray, - instance: String, - ) { - val messageStr = message.decodeToString() - Log.d(TAG, "New message $messageStr for Instance: $instance") - scope.launch { - try { - parseMessage(messageStr)?.let { receiveIfNew(it) } - } catch (e: Exception) { - if (e is CancellationException) throw e - Log.d(TAG, "Message could not be parsed: ${e.message}") - } - } - } - - private suspend fun parseMessage(message: String): GiftWrapEvent? { - (Event.fromJson(message) as? GiftWrapEvent)?.let { - return it - } - return null - } - - private suspend fun receiveIfNew(event: GiftWrapEvent) { - if (eventCache.get(event.id) == null) { - eventCache.put(event.id, event.id) - EventNotificationConsumer(appContext).consume(event) - } - } - - override fun onNewEndpoint( - context: Context, - endpoint: String, - instance: String, - ) { - Log.d(TAG, "New endpoint provided:- $endpoint for Instance: $instance") - val sanitizedEndpoint = if (endpoint.endsWith("?up=1")) endpoint.dropLast(5) else endpoint - if (pushHandler.getSavedEndpoint() == sanitizedEndpoint) { - Log.d(TAG, "Endpoint already saved. ") - return - } - pushHandler.setEndpoint(sanitizedEndpoint) - scope.launch(Dispatchers.IO) { - RegisterAccounts(LocalPreferences.allSavedAccounts(context)).go(sanitizedEndpoint) - NotificationUtils.getOrCreateDMChannel(appContext) - } - } - - override fun onRegistrationFailed( - context: Context, - instance: String, - ) { - Log.d(TAG, "Registration failed for Instance: $instance") - - LocalPreferences.allSavedAccounts(context).forEach { - NostrSigner.getInstance().getDatabase(it.npub).applicationDao().insertLog( - LogEntity( - 0, - "Push server", - "Push server", - "Registration failed for Instance: $instance", - System.currentTimeMillis(), - ), - ) - } - - scope.cancel() - pushHandler.forceRemoveDistributor(context) - } - - override fun onUnregistered( - context: Context, - instance: String, - ) { - val removedEndpoint = pushHandler.getSavedEndpoint() - Log.d(TAG, "Endpoint: $removedEndpoint removed for Instance: $instance") - Log.d(TAG, "App is unregistered. ") - pushHandler.forceRemoveDistributor(context) - pushHandler.removeEndpoint() - } -} diff --git a/app/src/free/java/com/greenart7c3/nostrsigner/service/PushNotificationUtils.kt b/app/src/free/java/com/greenart7c3/nostrsigner/service/PushNotificationUtils.kt deleted file mode 100644 index 784854e4..00000000 --- a/app/src/free/java/com/greenart7c3/nostrsigner/service/PushNotificationUtils.kt +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright (c) 2024 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.greenart7c3.nostrsigner.service - -import android.content.Intent -import android.content.Intent.FLAG_ACTIVITY_NEW_TASK -import android.net.Uri -import android.util.Log -import com.greenart7c3.nostrsigner.AccountInfo -import com.greenart7c3.nostrsigner.LocalPreferences -import com.greenart7c3.nostrsigner.NostrSigner -import com.greenart7c3.nostrsigner.R -import com.greenart7c3.nostrsigner.database.LogEntity -import com.greenart7c3.nostrsigner.ui.AccountStateViewModel -import com.greenart7c3.nostrsigner.ui.NotificationType -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch - -object PushNotificationUtils { - var hasInit: Boolean = false - private val pushHandler = PushDistributorHandler - var accountState: AccountStateViewModel? = null - - @OptIn(DelicateCoroutinesApi::class) - suspend fun init(accounts: List) { - if (hasInit || NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT) { - return - } - - val savedDistributor = pushHandler.getSavedDistributor() - if (savedDistributor.isBlank()) { - val distributions = pushHandler.getInstalledDistributors() - if (distributions.isNotEmpty()) { - distributions.firstOrNull { it.isNotBlank() }?.let { - pushHandler.saveDistributor(it) - } - } else { - accounts.forEach { - NostrSigner.getInstance().getDatabase(it.npub).applicationDao().insertLog( - LogEntity( - 0, - "Push server", - "Push server", - "No distributors found for push notifications", - System.currentTimeMillis(), - ), - ) - } - if (!NostrSigner.getInstance().settings.pushServerMessage && NostrSigner.getInstance().settings.notificationType != NotificationType.DIRECT) { - accountState?.toast( - title = NostrSigner.getInstance().getString(R.string.push_server), - message = NostrSigner.getInstance().getString(R.string.no_distributors_found), - onOk = { - GlobalScope.launch(Dispatchers.IO) { - NostrSigner.getInstance().settings = NostrSigner.getInstance().settings.copy(pushServerMessage = true) - LocalPreferences.saveSettingsToEncryptedStorage(NostrSigner.getInstance().settings) - } - - val intent = Intent(Intent.ACTION_VIEW) - intent.data = Uri.parse("https://ntfy.sh/") - intent.flags = FLAG_ACTIVITY_NEW_TASK - NostrSigner.getInstance().startActivity(intent) - }, - ) - } - } - } - try { - if (pushHandler.savedDistributorExists()) { - val endpoint = pushHandler.getSavedEndpoint() - if (endpoint.isBlank()) { - pushHandler.saveDistributor(pushHandler.getSavedDistributor()) - } - - RegisterAccounts(accounts).go(pushHandler.getSavedEndpoint()) - } - } catch (e: Exception) { - if (e is CancellationException) throw e - Log.d("Amber-OSSPushUtils", "Failed to get endpoint.") - accounts.forEach { - NostrSigner.getInstance().getDatabase(it.npub).applicationDao().insertLog( - LogEntity( - 0, - "Push server", - "Push server", - "Failed to get endpoint. ${e.message}", - System.currentTimeMillis(), - ), - ) - } - } - } -} diff --git a/app/src/free/java/com/greenart7c3/nostrsigner/ui/SelectNotificationProvider.kt b/app/src/free/java/com/greenart7c3/nostrsigner/ui/SelectNotificationProvider.kt deleted file mode 100644 index 012790e9..00000000 --- a/app/src/free/java/com/greenart7c3/nostrsigner/ui/SelectNotificationProvider.kt +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright (c) 2024 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.greenart7c3.nostrsigner.ui - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import com.greenart7c3.nostrsigner.R -import com.greenart7c3.nostrsigner.service.PushDistributorHandler -import com.greenart7c3.nostrsigner.ui.components.TitleExplainer -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList - -@Composable -fun LoadDistributors(onInner: @Composable (String, ImmutableList, ImmutableList) -> Unit) { - val currentDistributor = PushDistributorHandler.getSavedDistributor().ifBlank { null } ?: "None" - - val list = - remember { - PushDistributorHandler.getInstalledDistributors().plus("None").toImmutableList() - } - - val readableListWithExplainer = - PushDistributorHandler - .formattedDistributorNames() - .mapIndexed { index, name -> - TitleExplainer( - name, - stringResource(id = R.string.push_server_uses_app_explainer, list[index]), - ) - }.plus( - TitleExplainer( - stringResource(id = R.string.push_server_none), - stringResource(id = R.string.push_server_none_explainer), - ), - ) - .toImmutableList() - - onInner( - currentDistributor, - list, - readableListWithExplainer, - ) -} - -@Composable -fun PushNotificationSettingsRow() { - val context = LocalContext.current - LoadDistributors { currentDistributor, list, readableListWithExplainer -> - SettingsRow( - R.string.push_server_title, - R.string.push_server_explainer, - selectedItems = readableListWithExplainer, - selectedIndex = list.indexOf(currentDistributor), - ) { index -> - if (list[index] == "None") { - PushDistributorHandler.forceRemoveDistributor(context) - } else { - PushDistributorHandler.saveDistributor(list[index]) - } - } - } -} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/LocalPreferences.kt b/app/src/main/java/com/greenart7c3/nostrsigner/LocalPreferences.kt index b7c27c6d..896b32df 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/LocalPreferences.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/LocalPreferences.kt @@ -11,7 +11,6 @@ import com.greenart7c3.nostrsigner.database.ApplicationPermissionsEntity import com.greenart7c3.nostrsigner.models.Account import com.greenart7c3.nostrsigner.models.AmberSettings import com.greenart7c3.nostrsigner.ui.parseBiometricsTimeType -import com.greenart7c3.nostrsigner.ui.parseNotificationType import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES import com.vitorpamplona.ammolite.relays.RelaySetupInfo import com.vitorpamplona.ammolite.service.HttpClientManager @@ -116,7 +115,6 @@ object LocalPreferences { putStringSet(PrefKeys.DEFAULT_RELAYS, settings.defaultRelays.map { it.url }.toSet()) putLong(PrefKeys.LAST_BIOMETRICS_TIME, settings.lastBiometricsTime) putBoolean(PrefKeys.USE_AUTH, settings.useAuth) - putInt(PrefKeys.NOTIFICATION_TYPE, settings.notificationType.screenCode) putInt(PrefKeys.BIOMETRICS_TYPE, settings.biometricsTimeType.screenCode) putBoolean(PrefKeys.USE_PIN, settings.usePin) }.apply() @@ -156,7 +154,6 @@ object LocalPreferences { } ?: listOf(RelaySetupInfo("wss://relay.nsec.app", read = true, write = true, feedTypes = COMMON_FEED_TYPES)), lastBiometricsTime = getLong(PrefKeys.LAST_BIOMETRICS_TIME, 0), useAuth = getBoolean(PrefKeys.USE_AUTH, false), - notificationType = parseNotificationType(getInt(PrefKeys.NOTIFICATION_TYPE, 1)), biometricsTimeType = parseBiometricsTimeType(getInt(PrefKeys.BIOMETRICS_TYPE, 0)), usePin = getBoolean(PrefKeys.USE_PIN, false), ) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/NostrSigner.kt b/app/src/main/java/com/greenart7c3/nostrsigner/NostrSigner.kt index 93ee2751..90ba7e3f 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/NostrSigner.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/NostrSigner.kt @@ -12,9 +12,7 @@ import com.greenart7c3.nostrsigner.database.AppDatabase import com.greenart7c3.nostrsigner.models.AmberSettings import com.greenart7c3.nostrsigner.service.ConnectivityService import com.greenart7c3.nostrsigner.service.RelayDisconnectService -import com.greenart7c3.nostrsigner.ui.NotificationType import com.vitorpamplona.ammolite.relays.Client -import com.vitorpamplona.ammolite.relays.Relay import com.vitorpamplona.ammolite.relays.RelayPool import com.vitorpamplona.ammolite.relays.RelaySetupInfo import com.vitorpamplona.ammolite.relays.RelaySetupInfoToConnect @@ -122,28 +120,11 @@ class NostrSigner : Application() { LocalPreferences.loadFromEncryptedStorage(this, it.npub)?.useProxy ?: false } - if (settings.notificationType != NotificationType.DIRECT) { - RelayPool.register(Client) - savedRelays.forEach { setupInfo -> - if (RelayPool.getRelay(setupInfo.url) == null) { - RelayPool.addRelay( - Relay( - setupInfo.url, - setupInfo.read, - setupInfo.write, - if (isPrivateIp(setupInfo.url)) false else useProxy, - setupInfo.feedTypes, - ), - ) - } - } - } - if (shouldReconnect) { checkIfRelaysAreConnected() } @Suppress("KotlinConstantConditions") - if (settings.notificationType == NotificationType.DIRECT && BuildConfig.FLAVOR != "offline" && savedRelays.isNotEmpty()) { + if (BuildConfig.FLAVOR != "offline" && savedRelays.isNotEmpty()) { Client.reconnect( savedRelays.map { RelaySetupInfoToConnect(it.url, if (isPrivateIp(it.url)) false else useProxy, it.read, it.write, it.feedTypes) }.toTypedArray(), true, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/database/ApplicationDao.kt b/app/src/main/java/com/greenart7c3/nostrsigner/database/ApplicationDao.kt index 83cba61c..bd002945 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/database/ApplicationDao.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/database/ApplicationDao.kt @@ -26,6 +26,9 @@ interface ApplicationDao { @Query("SELECT * FROM application where pubKey = :pubKey order by name") fun getAll(pubKey: String): List + @Query("SELECT * FROM application where pubKey = :pubKey order by name") + fun getAllFlow(pubKey: String): Flow> + @Query("SELECT * FROM application WHERE `key` = :key") @Transaction fun getByKey(key: String): ApplicationWithPermissions? diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/models/AmberSettings.kt b/app/src/main/java/com/greenart7c3/nostrsigner/models/AmberSettings.kt index 16836a6c..2e6a6c57 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/models/AmberSettings.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/models/AmberSettings.kt @@ -1,7 +1,6 @@ package com.greenart7c3.nostrsigner.models import com.greenart7c3.nostrsigner.ui.BiometricsTimeType -import com.greenart7c3.nostrsigner.ui.NotificationType import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES import com.vitorpamplona.ammolite.relays.RelaySetupInfo @@ -11,7 +10,6 @@ data class AmberSettings( val defaultRelays: List = listOf(RelaySetupInfo("wss://relay.nsec.app", read = true, write = true, feedTypes = COMMON_FEED_TYPES)), val lastBiometricsTime: Long = 0, val useAuth: Boolean = false, - val notificationType: NotificationType = NotificationType.DIRECT, val biometricsTimeType: BiometricsTimeType = BiometricsTimeType.EVERY_TIME, val usePin: Boolean = false, ) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/ConnectivityService.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/ConnectivityService.kt index 1cafcc7f..8b7855ea 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/ConnectivityService.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/ConnectivityService.kt @@ -13,10 +13,8 @@ import android.os.IBinder import android.util.Log import androidx.core.app.NotificationCompat import com.greenart7c3.nostrsigner.BuildConfig -import com.greenart7c3.nostrsigner.LocalPreferences import com.greenart7c3.nostrsigner.NostrSigner import com.greenart7c3.nostrsigner.R -import com.greenart7c3.nostrsigner.ui.NotificationType import com.vitorpamplona.ammolite.relays.RelayPool import java.util.Timer import java.util.TimerTask @@ -40,7 +38,7 @@ class ConnectivityService : Service() { @Suppress("KotlinConstantConditions") if (BuildConfig.FLAVOR == "offline") return - if (lastNetwork != null && lastNetwork != network && NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT) { + if (lastNetwork != null && lastNetwork != network) { scope.launch(Dispatchers.IO) { NotificationDataSource.stopSync() delay(1000) @@ -66,7 +64,7 @@ class ConnectivityService : Service() { "ServiceManager NetworkCallback", "onCapabilitiesChanged: ${network.networkHandle} hasMobileData ${NostrSigner.getInstance().isOnMobileDataState.value} hasWifi ${NostrSigner.getInstance().isOnWifiDataState.value}", ) - if (NostrSigner.getInstance().updateNetworkCapabilities(networkCapabilities) && NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT) { + if (NostrSigner.getInstance().updateNetworkCapabilities(networkCapabilities)) { NotificationDataSource.stopSync() delay(1000) NotificationDataSource.start() @@ -101,11 +99,6 @@ class ConnectivityService : Service() { isStarted = true startForeground(1, createNotification()) - scope.launch(Dispatchers.IO) { - PushNotificationUtils.hasInit = false - PushNotificationUtils.init(LocalPreferences.allSavedAccounts(this@ConnectivityService)) - } - @Suppress("KotlinConstantConditions") if (BuildConfig.FLAVOR != "offline") { val connectivityManager = @@ -119,9 +112,11 @@ class ConnectivityService : Service() { timer.schedule( object : TimerTask() { override fun run() { - if (NostrSigner.getInstance().settings.notificationType == NotificationType.PUSH) return + @Suppress("KotlinConstantConditions") + if (BuildConfig.FLAVOR == "offline") { + return + } - Log.d("ConnectivityService", "Checking connectivity...") RelayPool.getAll().forEach { if (!it.isConnected()) { Log.d( diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt index 51fa1522..f38688ad 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/EventNotificationConsumer.kt @@ -42,13 +42,10 @@ import com.greenart7c3.nostrsigner.models.Permission import com.greenart7c3.nostrsigner.models.SignerType import com.greenart7c3.nostrsigner.service.NotificationUtils.sendNotification import com.greenart7c3.nostrsigner.service.model.AmberEvent -import com.greenart7c3.nostrsigner.ui.NotificationType import com.vitorpamplona.quartz.crypto.nip04.Nip04 import com.vitorpamplona.quartz.encoders.Hex import com.vitorpamplona.quartz.encoders.toNpub import com.vitorpamplona.quartz.events.Event -import com.vitorpamplona.quartz.events.GiftWrapEvent -import com.vitorpamplona.quartz.events.SealedGossipEvent class EventNotificationConsumer(private val applicationContext: Context) { @@ -64,61 +61,6 @@ class EventNotificationConsumer(private val applicationContext: Context) { } } - fun consume(event: GiftWrapEvent) { - if (!notificationManager().areNotificationsEnabled()) return - if (!event.hasCorrectIDHash()) return - if (!event.hasVerifiedSignature()) return - - // PushNotification Wraps don't include a receiver. - // Test with all logged in accounts - LocalPreferences.allSavedAccounts(applicationContext).forEach { - if (it.hasPrivKey) { - LocalPreferences.loadFromEncryptedStorage(applicationContext, it.npub)?.let { acc -> - if (NostrSigner.getInstance().settings.notificationType == NotificationType.PUSH) { - consumeIfMatchesAccount(event, acc) - } - } - } - } - } - - private fun consumeIfMatchesAccount( - pushWrappedEvent: GiftWrapEvent, - account: Account, - ) { - pushWrappedEvent.unwrap(account.signer) { notificationEvent -> - unwrapAndConsume(notificationEvent, account) { innerEvent -> - if (innerEvent.kind == 24133) { - if (innerEvent.hasCorrectIDHash()) { - if (innerEvent.hasVerifiedSignature()) { - notify(innerEvent, account) - } - } - } - } - } - } - - private fun unwrapAndConsume( - event: Event, - account: Account, - onReady: (Event) -> Unit, - ) { - when (event) { - is GiftWrapEvent -> { - event.unwrap(account.signer) { unwrapAndConsume(it, account, onReady) } - } - is SealedGossipEvent -> { - event.unseal(account.signer) { - onReady(it) - } - } - else -> { - onReady(event) - } - } - } - private fun notify( event: Event, acc: Account, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt index 7d88c09a..e20b6cf1 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/IntentUtils.kt @@ -34,7 +34,6 @@ import com.greenart7c3.nostrsigner.models.TimeUtils import com.greenart7c3.nostrsigner.models.containsNip import com.greenart7c3.nostrsigner.relays.AmberListenerSingleton import com.greenart7c3.nostrsigner.service.model.AmberEvent -import com.greenart7c3.nostrsigner.ui.NotificationType import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES import com.vitorpamplona.ammolite.relays.Client import com.vitorpamplona.ammolite.relays.RelayPool @@ -388,15 +387,11 @@ object IntentUtils { GlobalScope.launch(Dispatchers.IO) { if (RelayPool.getAll().any { !it.isConnected() }) { NostrSigner.getInstance().checkForNewRelays( - NostrSigner.getInstance().settings.notificationType != NotificationType.DIRECT, newRelays = relays.toSet(), ) } val success = Client.sendAndWaitForResponse(it, relayList = relays) - if (NostrSigner.getInstance().settings.notificationType != NotificationType.DIRECT) { - RelayPool.unregister(Client) - } if (success) { onDone() } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/NotificationDataSource.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/NotificationDataSource.kt index a0ae9a17..84fdc07a 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/NotificationDataSource.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/service/NotificationDataSource.kt @@ -25,7 +25,6 @@ import com.greenart7c3.nostrsigner.NostrSigner import com.greenart7c3.nostrsigner.checkNotInMainThread import com.greenart7c3.nostrsigner.database.LogEntity import com.greenart7c3.nostrsigner.models.TimeUtils -import com.greenart7c3.nostrsigner.ui.NotificationType import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES import com.vitorpamplona.ammolite.relays.Client import com.vitorpamplona.ammolite.relays.NostrDataSource @@ -143,9 +142,7 @@ object NotificationDataSource : NostrDataSource("AccountData") { init { scope.launch { LocalPreferences.loadSettingsFromEncryptedStorage() - if (NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT) { - Client.subscribe(clientListener) - } + Client.subscribe(clientListener) } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/service/RegisterAccounts.kt b/app/src/main/java/com/greenart7c3/nostrsigner/service/RegisterAccounts.kt deleted file mode 100644 index b12fb200..00000000 --- a/app/src/main/java/com/greenart7c3/nostrsigner/service/RegisterAccounts.kt +++ /dev/null @@ -1,228 +0,0 @@ -/** - * Copyright (c) 2024 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.greenart7c3.nostrsigner.service - -import android.util.Log -import com.greenart7c3.nostrsigner.AccountInfo -import com.greenart7c3.nostrsigner.BuildConfig -import com.greenart7c3.nostrsigner.LocalPreferences -import com.greenart7c3.nostrsigner.NostrSigner -import com.greenart7c3.nostrsigner.database.LogEntity -import com.greenart7c3.nostrsigner.models.Account -import com.vitorpamplona.ammolite.service.HttpClientManager -import com.vitorpamplona.quartz.encoders.toHexKey -import com.vitorpamplona.quartz.encoders.toNpub -import com.vitorpamplona.quartz.events.RelayAuthEvent -import java.net.URL -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody - -class RegisterAccounts( - private val accounts: List, -) { - private fun recursiveAuthCreation( - notificationToken: String, - remainingTos: List>, - output: MutableList, - onReady: (List) -> Unit, - ) { - if (remainingTos.isEmpty()) { - onReady(output) - return - } - - val next = remainingTos.first() - - next.first.createAuthEvent(next.second, notificationToken) { - output.add(it) - recursiveAuthCreation(notificationToken, remainingTos.filter { remainingTo -> next != remainingTo }, output, onReady) - } - } - - private fun isSupportedUrl(url: String): Boolean { - return url.isNotBlank() && - !url.contains("brb.io") && // no broken relays - !url.contains("echo.websocket.org") && // test relay - !url.contains("127.0") && // no local relays - !url.contains("umbrel.local") && // no local relays - !url.contains("192.168.") && // no local relays - !url.contains(".onion") && // we are not running on Tor - !url.contains("https://") && // not a websocket - !url.contains("http://") && // not a websocket - !url.contains("www://") && // not a websocket - !url.contains("https//") && // not a websocket - !url.contains("http//") && // not a websocket - !url.contains("www//") && // not a websocket - !url.contains("npub1") && // does not allow custom uris - !url.contains("was://") && // common mispellings - !url.contains("ws://umbrel:") && // local domain - !url.contains("\t") && // tab is not allowed - !url.contains(" ") && // space is not allowed - isValidUrl(url) - } - - private fun isValidUrl(url: String): Boolean { - try { - URL(url.replace("wss://", "https://").replace("ws://", "http://")) - return true - } catch (e: Exception) { - return false - } - } - - // creates proof that it controls all accounts - private fun signEventsToProveControlOfAccounts( - accounts: List, - notificationToken: String, - onReady: (List) -> Unit, - ) { - val readyToSend: MutableSet> = mutableSetOf() - accounts.forEach { - val acc = LocalPreferences.loadFromEncryptedStorage(NostrSigner.getInstance(), it.npub) - if (acc != null) { - val permissions = - NostrSigner.getInstance().getDatabase( - acc.signer.keyPair.pubKey.toNpub(), - ).applicationDao().getAll(acc.signer.keyPair.pubKey.toHexKey()) - permissions.forEach { permission -> - permission.relays.forEach { relay -> - if (isSupportedUrl(relay.url)) { - readyToSend.add( - Pair(acc, relay.url), - ) - } - } - } - - runBlocking { - LocalPreferences.loadSettingsFromEncryptedStorage() - } - val defaultRelays = NostrSigner.getInstance().settings.defaultRelays - defaultRelays.forEach { relay -> - if (isSupportedUrl(relay.url)) { - readyToSend.add( - Pair(acc, relay.url), - ) - } - } - } - } - - val listOfAuthEvents = mutableListOf() - recursiveAuthCreation( - notificationToken, - readyToSend.toList(), - listOfAuthEvents, - onReady, - ) - } - - private fun postRegistrationEvent(events: List, notificationToken: String) { - if (notificationToken.isBlank()) { - Log.d("FirebaseMsgService", "No notification token to register with push server") - return - } - try { - val jsonObject = - """{ - "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", "Amber/${BuildConfig.VERSION_NAME}") - .url("https://push.greenart7c3.com/register") - .post(body) - .build() - - val useProxy = accounts.any { - val account = LocalPreferences.loadFromEncryptedStorage(context = NostrSigner.getInstance(), npub = it.npub) - val useProxy = account?.useProxy ?: false - - if (useProxy) { - HttpClientManager.setDefaultProxyOnPort(account?.proxyPort ?: 9050) - } - - useProxy - } - - val client = HttpClientManager.getHttpClient(useProxy) - var isSuccess = client.newCall(request).execute().use { - it.isSuccessful - } - var tries = 0 - while (!isSuccess && tries < 3) { - Log.d("FirebaseMsgService", "Failed to register with push server, retrying...") - isSuccess = client.newCall(request).execute().use { - it.isSuccessful - } - tries++ - } - if (isSuccess) { - Log.d("FirebaseMsgService", "Successfully registered with push server") - } else { - accounts.forEach { - NostrSigner.getInstance().getDatabase(it.npub).applicationDao().insertLog( - LogEntity( - 0, - "Push server", - "Push server", - "Failed to register with push server", - System.currentTimeMillis(), - ), - ) - } - } - } catch (e: java.lang.Exception) { - if (e is CancellationException) throw e - val tag = "FirebaseMsgService" - - Log.e(tag, "Unable to register with push server", e) - accounts.forEach { - NostrSigner.getInstance().getDatabase(it.npub).applicationDao().insertLog( - LogEntity( - 0, - "Push server", - "Push server", - "Unable to register with push server: ${e.message}", - System.currentTimeMillis(), - ), - ) - } - } - } - - suspend fun go(notificationToken: String) = - withContext(Dispatchers.IO) { - signEventsToProveControlOfAccounts(accounts, notificationToken) { postRegistrationEvent(it, notificationToken) } - - PushNotificationUtils.hasInit = true - } -} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt index 7608c6d2..b0f36662 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/AccountScreen.kt @@ -9,8 +9,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material.icons.Icons @@ -19,10 +17,7 @@ import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.CenterAlignedTopAppBar -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -49,14 +44,12 @@ import com.greenart7c3.nostrsigner.relays.AmberListenerSingleton import com.greenart7c3.nostrsigner.service.ConnectivityService import com.greenart7c3.nostrsigner.service.IntentUtils import com.greenart7c3.nostrsigner.service.NotificationDataSource -import com.greenart7c3.nostrsigner.service.PushNotificationUtils import com.vitorpamplona.quartz.encoders.toNpub import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3Api::class) @SuppressLint("StateFlowValueCalledInComposition", "UnrememberedMutableState") @Composable fun AccountScreen( @@ -82,115 +75,69 @@ fun AccountScreen( MainLoginPage(accountStateViewModel) } is AccountState.LoggedIn -> { - var isLoading by remember { mutableStateOf(true) } - var isNotificationsConfigured by remember { - mutableStateOf(false) + var intentData by remember { + mutableStateOf(null) } - LaunchedEffect(Unit) { - launch(Dispatchers.IO) { - isNotificationsConfigured = LocalPreferences.isNotificationTypeConfigured() - isLoading = false + LaunchedEffect(intent, intents) { + IntentUtils.getIntentData(context, intent, packageName, intent.getStringExtra("route"), state.account) { + intentData = it + } + val data = + intents.firstOrNull { + it.currentAccount.isNotBlank() + } + + data?.bunkerRequest?.let { + if (it.currentAccount.isNotBlank()) { + if (LocalPreferences.currentAccount(context) != it.currentAccount) { + accountStateViewModel.switchUser(it.currentAccount, null) + } + } } } - if (isLoading) { - Scaffold { innerPadding -> - CenterCircularProgressIndicator( - Modifier - .fillMaxSize() - .padding(innerPadding), - ) + if (intentData != null) { + if (intents.none { item -> item.id == intentData!!.id }) { + val oldIntents = intents.toMutableList() + oldIntents.add(intentData!!) + flow.value = oldIntents } - } else { - if (isNotificationsConfigured) { - var intentData by remember { - mutableStateOf(null) + } + + val newIntents = + intents.ifEmpty { + if (intentData == null) { + listOf() + } else { + listOf(intentData) } - LaunchedEffect(intent, intents) { - IntentUtils.getIntentData(context, intent, packageName, intent.getStringExtra("route"), state.account) { - intentData = it - } - val data = - intents.firstOrNull { - it.currentAccount.isNotBlank() - } + }.mapNotNull { it } + val database = NostrSigner.getInstance().getDatabase(state.account.signer.keyPair.pubKey.toNpub()) + val localRoute = mutableStateOf(newIntents.firstNotNullOfOrNull { it.route } ?: state.route) - data?.bunkerRequest?.let { - if (it.currentAccount.isNotBlank()) { - if (LocalPreferences.currentAccount(context) != it.currentAccount) { - accountStateViewModel.switchUser(it.currentAccount, null) - } - } - } - } - - if (intentData != null) { - if (intents.none { item -> item.id == intentData!!.id }) { - val oldIntents = intents.toMutableList() - oldIntents.add(intentData!!) - flow.value = oldIntents - } - } - - val newIntents = - intents.ifEmpty { - if (intentData == null) { - listOf() - } else { - listOf(intentData) - } - }.mapNotNull { it } - val database = NostrSigner.getInstance().getDatabase(state.account.signer.keyPair.pubKey.toNpub()) - val localRoute = mutableStateOf(newIntents.firstNotNullOfOrNull { it.route } ?: state.route) - - SideEffect { - NostrSigner.getInstance().applicationIOScope.launch(Dispatchers.IO) { - PushNotificationUtils.accountState = accountStateViewModel - try { - NostrSigner.getInstance().applicationContext.startForegroundService( - Intent(NostrSigner.getInstance().applicationContext, ConnectivityService::class.java), - ) - } catch (e: Exception) { - Log.d("NostrSigner", "Failed to start ConnectivityService", e) - } - - @Suppress("KotlinConstantConditions") - if (NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT && BuildConfig.FLAVOR != "offline") { - NostrSigner.getInstance().checkForNewRelays() - NotificationDataSource.start() - delay(5000) - } - } - } - - AmberListenerSingleton.accountStateViewModel = accountStateViewModel - - DisplayErrorMessages(accountStateViewModel) - MainScreen(state.account, accountStateViewModel, newIntents, packageName, appName, localRoute, database, navController) - } else { - AmberListenerSingleton.accountStateViewModel = accountStateViewModel - DisplayErrorMessages(accountStateViewModel) - Scaffold( - topBar = { - CenterAlignedTopAppBar( - title = { - Text(stringResource(R.string.notification_type)) - }, - ) - }, - ) { innerPadding -> - NotificationTypeScreen( - Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(40.dp), - onDone = { - isNotificationsConfigured = true - }, + SideEffect { + NostrSigner.getInstance().applicationIOScope.launch(Dispatchers.IO) { + try { + NostrSigner.getInstance().applicationContext.startForegroundService( + Intent(NostrSigner.getInstance().applicationContext, ConnectivityService::class.java), ) + } catch (e: Exception) { + Log.d("NostrSigner", "Failed to start ConnectivityService", e) + } + + @Suppress("KotlinConstantConditions") + if (BuildConfig.FLAVOR != "offline") { + NostrSigner.getInstance().checkForNewRelays() + NotificationDataSource.start() + delay(5000) } } } + + AmberListenerSingleton.accountStateViewModel = accountStateViewModel + + DisplayErrorMessages(accountStateViewModel) + MainScreen(state.account, accountStateViewModel, newIntents, packageName, appName, localRoute, database, navController) } } } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditConfigurationScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditConfigurationScreen.kt index 25915684..08db35ff 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditConfigurationScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/EditConfigurationScreen.kt @@ -162,9 +162,8 @@ fun EditConfigurationScreen( it.permissions, ), ) - if (NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT) { - NostrSigner.getInstance().checkForNewRelays() - } + + NostrSigner.getInstance().checkForNewRelays() } } }, @@ -203,9 +202,7 @@ fun EditConfigurationScreen( it.permissions, ), ) - if (NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT) { - NostrSigner.getInstance().checkForNewRelays() - } + NostrSigner.getInstance().checkForNewRelays() } } }, @@ -255,9 +252,7 @@ fun EditConfigurationScreen( it.permissions, ), ) - if (NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT) { - NostrSigner.getInstance().checkForNewRelays() - } + NostrSigner.getInstance().checkForNewRelays() } } }, @@ -280,9 +275,7 @@ fun EditConfigurationScreen( scope.launch(Dispatchers.IO) { database.applicationDao().delete(it.application) - if (NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT) { - NostrSigner.getInstance().checkForNewRelays() - } + NostrSigner.getInstance().checkForNewRelays() scope.launch(Dispatchers.Main) { navController.navigate(Route.Applications.route) { diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt index 63c6a09b..3946a486 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/MainScreen.kt @@ -86,7 +86,6 @@ import com.greenart7c3.nostrsigner.models.TimeUtils import com.greenart7c3.nostrsigner.models.basicPermissions import com.greenart7c3.nostrsigner.service.EventNotificationConsumer import com.greenart7c3.nostrsigner.service.IntentUtils -import com.greenart7c3.nostrsigner.service.PushNotificationUtils import com.greenart7c3.nostrsigner.service.getAppCompatActivity import com.greenart7c3.nostrsigner.ui.actions.AccountBackupScreen import com.greenart7c3.nostrsigner.ui.actions.AccountsBottomSheet @@ -131,8 +130,7 @@ fun sendResult( val relays = savedApplication?.application?.relays?.ifEmpty { defaultRelays } ?: (intentData.bunkerRequest?.relays?.ifEmpty { defaultRelays } ?: defaultRelays) if (intentData.bunkerRequest != null) { NostrSigner.getInstance().checkForNewRelays( - NostrSigner.getInstance().settings.notificationType != NotificationType.DIRECT, - relays.toSet(), + newRelays = relays.toSet(), ) } @@ -259,10 +257,6 @@ fun sendResult( true, ), ) - PushNotificationUtils.hasInit = false - GlobalScope.launch(Dispatchers.IO) { - PushNotificationUtils.init(LocalPreferences.allSavedAccounts(context)) - } EventNotificationConsumer(context).notificationManager().cancelAll() activity?.intent = null @@ -375,9 +369,6 @@ private suspend fun askNotificationPermission( onShouldShowRequestPermissionRationale: () -> Unit, ) { if (ContextCompat.checkSelfPermission(context, "android.permission.POST_NOTIFICATIONS") == PackageManager.PERMISSION_GRANTED) { - initNotifications( - context = context, - ) return } @@ -398,11 +389,6 @@ private suspend fun askNotificationPermission( } } -private suspend fun initNotifications(context: Context) { - PushNotificationUtils.hasInit = false - PushNotificationUtils.init(LocalPreferences.allSavedAccounts(context)) -} - fun Color.Companion.fromHex(colorString: String) = try { Color(android.graphics.Color.parseColor("#$colorString")) } catch (e: Exception) { @@ -431,13 +417,7 @@ fun MainScreen( rememberLauncherForActivityResult( ActivityResultContracts.RequestPermission(), ) { isGranted: Boolean -> - if (isGranted) { - scope.launch(Dispatchers.IO) { - initNotifications( - context = context, - ) - } - } else { + if (!isGranted) { if (LocalPreferences.shouldShowRationale(context) == null) { LocalPreferences.updateShouldShowRationale(context, true) } @@ -682,7 +662,6 @@ fun MainScreen( .padding(padding) .padding(24.dp), account = account, - accountStateViewModel = accountStateViewModel, navController = navController, database = database, ) @@ -779,22 +758,6 @@ fun MainScreen( }, ) - composable( - Route.NotificationType.route, - content = { - NotificationTypeScreen( - modifier = - Modifier - .fillMaxSize() - .padding(padding) - .padding(40.dp), - onDone = { - navController.navigateUp() - }, - ) - }, - ) - composable( Route.DefaultRelays.route, content = { diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/NotificationTypeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/NotificationTypeScreen.kt deleted file mode 100644 index d748be56..00000000 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/NotificationTypeScreen.kt +++ /dev/null @@ -1,133 +0,0 @@ -package com.greenart7c3.nostrsigner.ui - -import android.content.Intent -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import com.greenart7c3.nostrsigner.BuildConfig -import com.greenart7c3.nostrsigner.LocalPreferences -import com.greenart7c3.nostrsigner.NostrSigner -import com.greenart7c3.nostrsigner.R -import com.greenart7c3.nostrsigner.relays.AmberListenerSingleton -import com.greenart7c3.nostrsigner.service.ConnectivityService -import com.greenart7c3.nostrsigner.service.NotificationDataSource -import com.greenart7c3.nostrsigner.service.PushNotificationUtils -import com.greenart7c3.nostrsigner.ui.components.AmberButton -import com.greenart7c3.nostrsigner.ui.components.TitleExplainer -import com.vitorpamplona.ammolite.relays.RelayPool -import kotlinx.collections.immutable.persistentListOf -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -@Composable -fun NotificationTypeScreen( - modifier: Modifier = Modifier, - onDone: () -> Unit, -) { - val context = LocalContext.current - - LaunchedEffect(Unit) { - launch(Dispatchers.IO) { - if (!LocalPreferences.isNotificationTypeConfigured()) { - AmberListenerSingleton.accountStateViewModel?.toast( - context.getString(R.string.notification_settings), - context.getString(R.string.please_configure_your_notification_settings_before_continuing), - ) - } - } - } - - val scope = rememberCoroutineScope() - var notificationItemsIndex by remember { - mutableIntStateOf(NostrSigner.getInstance().settings.notificationType.screenCode) - } - val notificationItems = - persistentListOf( - TitleExplainer( - stringResource(NotificationType.PUSH.resourceId), - stringResource(R.string.push_notifications_explainer), - ), - TitleExplainer( - stringResource(NotificationType.DIRECT.resourceId), - stringResource(R.string.direct_notifications_explainer), - ), - ) - Column( - modifier = modifier - .fillMaxSize(), - ) { - Column( - Modifier.weight(1f), - ) { - Box( - Modifier - .padding(8.dp), - ) { - SettingsRow( - R.string.notification_type, - R.string.select_the_type_of_notification_you_want_to_receive, - notificationItems, - notificationItemsIndex, - ) { - notificationItemsIndex = it - } - } - - @Suppress("KotlinConstantConditions") - if (BuildConfig.FLAVOR == "free") { - Box( - Modifier - .padding(8.dp), - ) { - PushNotificationSettingsRow() - } - } - } - AmberButton( - onClick = { - scope.launch(Dispatchers.IO) { - NostrSigner.getInstance().settings = NostrSigner.getInstance().settings.copy( - notificationType = parseNotificationType(notificationItemsIndex), - ) - LocalPreferences.saveSettingsToEncryptedStorage(NostrSigner.getInstance().settings) - PushNotificationUtils.hasInit = false - PushNotificationUtils.init(LocalPreferences.allSavedAccounts(context)) - if (notificationItemsIndex == 0) { - NotificationDataSource.stopSync() - RelayPool.disconnect() - } else { - NostrSigner.getInstance().checkForNewRelays() - NotificationDataSource.start() - } - NostrSigner.getInstance().applicationContext.startForegroundService( - Intent( - NostrSigner.getInstance().applicationContext, - ConnectivityService::class.java, - ), - ) - scope.launch(Dispatchers.Main) { - onDone() - } - } - }, - content = { - Text( - text = stringResource(R.string.save), - ) - }, - ) - } -} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/PermissionsScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/PermissionsScreen.kt index f8dfa983..dd7ddbcb 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/PermissionsScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/PermissionsScreen.kt @@ -12,57 +12,35 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.greenart7c3.nostrsigner.LocalPreferences import com.greenart7c3.nostrsigner.R import com.greenart7c3.nostrsigner.database.AppDatabase -import com.greenart7c3.nostrsigner.database.ApplicationEntity import com.greenart7c3.nostrsigner.models.Account import com.greenart7c3.nostrsigner.service.toShortenHex import com.vitorpamplona.quartz.encoders.toHexKey -import com.vitorpamplona.quartz.encoders.toNpub -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch @Composable fun PermissionsScreen( modifier: Modifier, account: Account, - accountStateViewModel: AccountStateViewModel, navController: NavController, database: AppDatabase, ) { - val lifecycleEvent = rememberLifecycleEvent() - val context = LocalContext.current - val localAccount = LocalPreferences.loadFromEncryptedStorage(context, account.signer.keyPair.pubKey.toNpub())!! - val applications = - remember { - mutableListOf() - } - val scope = rememberCoroutineScope() - // val sortBy by remember { mutableStateOf("name") } - - LaunchedEffect(lifecycleEvent) { - scope.launch(Dispatchers.IO) { - applications.clear() - applications.addAll(database.applicationDao().getAll(localAccount.signer.keyPair.pubKey.toHexKey())) - } - } + val applications = database.applicationDao().getAllFlow(account.signer.keyPair.pubKey.toHexKey()).collectAsStateWithLifecycle(emptyList()) Column( modifier, ) { - if (applications.isEmpty()) { + if (applications.value.isEmpty()) { Column( Modifier.fillMaxSize(), Arrangement.Center, @@ -100,13 +78,13 @@ fun PermissionsScreen( // ) // } // } - items(applications.size) { + items(applications.value.size) { Row( modifier = Modifier .fillMaxSize() .padding(vertical = 4.dp) .clickable { - navController.navigate("Permission/${applications.elementAt(it).key}") + navController.navigate("Permission/${applications.value.elementAt(it).key}") }, verticalAlignment = Alignment.CenterVertically, ) { @@ -115,7 +93,7 @@ fun PermissionsScreen( .fillMaxSize(), verticalArrangement = Arrangement.Center, ) { - val localPermission = applications.elementAt(it) + val localPermission = applications.value.elementAt(it) Text( modifier = Modifier.padding(top = 16.dp), text = localPermission.name.ifBlank { localPermission.key.toShortenHex() }, diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/SettingsScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/SettingsScreen.kt index acfbcfa5..dc1a3046 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/SettingsScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/SettingsScreen.kt @@ -18,7 +18,6 @@ import androidx.compose.material.icons.filled.FilterList import androidx.compose.material.icons.filled.Hub import androidx.compose.material.icons.filled.Key import androidx.compose.material.icons.filled.Language -import androidx.compose.material.icons.filled.Notifications import androidx.compose.material.icons.filled.Security import androidx.compose.material.icons.filled.SurroundSound import androidx.compose.material3.AlertDialog @@ -61,7 +60,6 @@ import com.greenart7c3.nostrsigner.ui.components.IconRow import com.greenart7c3.nostrsigner.ui.components.TextSpinner import com.greenart7c3.nostrsigner.ui.components.TitleExplainer import com.greenart7c3.nostrsigner.ui.navigation.Route -import com.vitorpamplona.ammolite.relays.RelayPool import com.vitorpamplona.quartz.encoders.toNpub import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.Dispatchers @@ -144,20 +142,6 @@ fun SettingsScreen( @Suppress("KotlinConstantConditions") if (BuildConfig.FLAVOR != "offline") { - Box( - Modifier - .padding(8.dp), - ) { - IconRow( - title = stringResource(R.string.notification_type), - icon = Icons.Default.Notifications, - tint = MaterialTheme.colorScheme.onBackground, - onClick = { - navController.navigate(Route.NotificationType.route) - }, - ) - } - Box( Modifier .padding(8.dp), @@ -182,43 +166,43 @@ fun SettingsScreen( }, ) } - } - Box( - Modifier - .padding(8.dp), - ) { - IconRow( - title = stringResource(R.string.default_relays), - icon = Icons.Default.Hub, - tint = MaterialTheme.colorScheme.onBackground, - onClick = { - navController.navigate(Route.DefaultRelays.route) - }, - ) - } + Box( + Modifier + .padding(8.dp), + ) { + IconRow( + title = stringResource(R.string.default_relays), + icon = Icons.Default.Hub, + tint = MaterialTheme.colorScheme.onBackground, + onClick = { + navController.navigate(Route.DefaultRelays.route) + }, + ) + } - Box( - Modifier - .padding(8.dp), - ) { - IconRow( - title = if (allowNewConnections) stringResource(R.string.disable_listening_for_new_connections) else stringResource(R.string.enable_listening_for_new_connections), - icon = Icons.Default.SurroundSound, - tint = MaterialTheme.colorScheme.onBackground, - onClick = { - allowNewConnections = !allowNewConnections - account.allowNewConnections = allowNewConnections - LocalPreferences.saveToEncryptedStorage(context, account) - }, - onLongClick = { - Toast.makeText( - context, - if (allowNewConnections) context.getString(R.string.disable_listening_for_new_connections) else context.getString(R.string.enable_listening_for_new_connections), - Toast.LENGTH_LONG, - ).show() - }, - ) + Box( + Modifier + .padding(8.dp), + ) { + IconRow( + title = if (allowNewConnections) stringResource(R.string.disable_listening_for_new_connections) else stringResource(R.string.enable_listening_for_new_connections), + icon = Icons.Default.SurroundSound, + tint = MaterialTheme.colorScheme.onBackground, + onClick = { + allowNewConnections = !allowNewConnections + account.allowNewConnections = allowNewConnections + LocalPreferences.saveToEncryptedStorage(context, account) + }, + onLongClick = { + Toast.makeText( + context, + if (allowNewConnections) context.getString(R.string.disable_listening_for_new_connections) else context.getString(R.string.enable_listening_for_new_connections), + Toast.LENGTH_LONG, + ).show() + }, + ) + } } Box( @@ -307,12 +291,8 @@ fun SettingsScreen( LocalPreferences.updateProxy(context, true, proxyPort.value.toInt()) scope.launch(Dispatchers.IO) { NotificationDataSource.stopSync() - if (NostrSigner.getInstance().settings.notificationType != NotificationType.DIRECT) { - RelayPool.disconnect() - } else { - NostrSigner.getInstance().checkForNewRelays() - NotificationDataSource.start() - } + NostrSigner.getInstance().checkForNewRelays() + NotificationDataSource.start() } }, onError = { @@ -341,12 +321,8 @@ fun SettingsScreen( LocalPreferences.updateProxy(context, false, proxyPort.value.toInt()) scope.launch(Dispatchers.IO) { NotificationDataSource.stopSync() - if (NostrSigner.getInstance().settings.notificationType != NotificationType.DIRECT) { - RelayPool.disconnect() - } else { - NostrSigner.getInstance().checkForNewRelays() - NotificationDataSource.start() - } + NostrSigner.getInstance().checkForNewRelays() + NotificationDataSource.start() } }, ) { @@ -406,11 +382,6 @@ fun SettingsRow( } } -enum class NotificationType(val screenCode: Int, val resourceId: Int) { - PUSH(0, R.string.push_notifications), - DIRECT(1, R.string.direct_connection), -} - enum class BiometricsTimeType(val screenCode: Int, val resourceId: Int) { EVERY_TIME(0, R.string.every_time), ONE_MINUTE(1, R.string.one_minute), @@ -428,13 +399,3 @@ fun parseBiometricsTimeType(screenCode: Int): BiometricsTimeType { } } } - -fun parseNotificationType(screenCode: Int): NotificationType { - return when (screenCode) { - NotificationType.PUSH.screenCode -> NotificationType.PUSH - NotificationType.DIRECT.screenCode -> NotificationType.DIRECT - else -> { - NotificationType.PUSH - } - } -} diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/EditRelaysDialog.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/EditRelaysDialog.kt index 88ba459b..c9c9064f 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/EditRelaysDialog.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/actions/EditRelaysDialog.kt @@ -60,7 +60,6 @@ import com.greenart7c3.nostrsigner.service.Nip11Retriever import com.greenart7c3.nostrsigner.service.NotificationDataSource import com.greenart7c3.nostrsigner.ui.AccountStateViewModel import com.greenart7c3.nostrsigner.ui.CenterCircularProgressIndicator -import com.greenart7c3.nostrsigner.ui.NotificationType import com.greenart7c3.nostrsigner.ui.components.AmberButton import com.vitorpamplona.ammolite.relays.COMMON_FEED_TYPES import com.vitorpamplona.ammolite.relays.Client @@ -149,7 +148,7 @@ fun DefaultRelaysScreen( LocalPreferences.saveSettingsToEncryptedStorage(NostrSigner.getInstance().settings) scope.launch(Dispatchers.IO) { @Suppress("KotlinConstantConditions") - if (NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT && BuildConfig.FLAVOR != "offline") { + if (BuildConfig.FLAVOR != "offline") { NostrSigner.getInstance().checkForNewRelays() NotificationDataSource.stop() delay(2000) @@ -191,7 +190,7 @@ fun DefaultRelaysScreen( LocalPreferences.saveSettingsToEncryptedStorage(NostrSigner.getInstance().settings) scope.launch(Dispatchers.IO) { @Suppress("KotlinConstantConditions") - if (NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT && BuildConfig.FLAVOR != "offline") { + if (BuildConfig.FLAVOR != "offline") { NostrSigner.getInstance().checkForNewRelays() NotificationDataSource.stop() delay(2000) @@ -238,7 +237,7 @@ fun DefaultRelaysScreen( LocalPreferences.saveSettingsToEncryptedStorage(NostrSigner.getInstance().settings) scope.launch(Dispatchers.IO) { @Suppress("KotlinConstantConditions") - if (NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT && BuildConfig.FLAVOR != "offline") { + if (BuildConfig.FLAVOR != "offline") { NostrSigner.getInstance().checkForNewRelays() NotificationDataSource.stop() delay(2000) diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt index a2278132..707352f9 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/MultiEventHomeScreen.kt @@ -61,7 +61,6 @@ import com.greenart7c3.nostrsigner.service.IntentUtils import com.greenart7c3.nostrsigner.service.getAppCompatActivity import com.greenart7c3.nostrsigner.service.model.AmberEvent import com.greenart7c3.nostrsigner.service.toShortenHex -import com.greenart7c3.nostrsigner.ui.NotificationType import com.greenart7c3.nostrsigner.ui.Result import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.encoders.toHexKey @@ -577,7 +576,7 @@ private fun sendResultIntent( private suspend fun reconnectToRelays(intents: List) { if (!intents.any { it.bunkerRequest != null }) return - NostrSigner.getInstance().checkForNewRelays(NostrSigner.getInstance().settings.notificationType != NotificationType.DIRECT) + NostrSigner.getInstance().checkForNewRelays() } @Composable diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt index 21c0d732..6c55ec27 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/components/SingleEventHomeScreen.kt @@ -36,7 +36,6 @@ import com.greenart7c3.nostrsigner.models.kindToNip import com.greenart7c3.nostrsigner.service.AmberUtils import com.greenart7c3.nostrsigner.service.getAppCompatActivity import com.greenart7c3.nostrsigner.service.toShortenHex -import com.greenart7c3.nostrsigner.ui.NotificationType import com.greenart7c3.nostrsigner.ui.sendResult import com.vitorpamplona.quartz.crypto.CryptoUtils import com.vitorpamplona.quartz.encoders.bechToBytes @@ -138,8 +137,7 @@ fun SingleEventHomeScreen( val relays = savedApplication?.application?.relays?.ifEmpty { defaultRelays } ?: intentData.bunkerRequest.relays.ifEmpty { defaultRelays } NostrSigner.getInstance().checkForNewRelays( - NostrSigner.getInstance().settings.notificationType != NotificationType.DIRECT, - relays.toSet(), + newRelays = relays.toSet(), ) AmberUtils.sendBunkerError( @@ -242,8 +240,7 @@ fun SingleEventHomeScreen( if (intentData.bunkerRequest != null) { NostrSigner.getInstance().checkForNewRelays( - NostrSigner.getInstance().settings.notificationType != NotificationType.DIRECT, - relays.toSet(), + newRelays = relays.toSet(), ) } @@ -378,8 +375,7 @@ fun SingleEventHomeScreen( if (intentData.bunkerRequest != null) { NostrSigner.getInstance().checkForNewRelays( - NostrSigner.getInstance().settings.notificationType != NotificationType.DIRECT, - relays.toSet(), + newRelays = relays.toSet(), ) } @@ -528,8 +524,7 @@ fun SingleEventHomeScreen( if (intentData.bunkerRequest != null) { NostrSigner.getInstance().checkForNewRelays( - NostrSigner.getInstance().settings.notificationType != NotificationType.DIRECT, - relays.toSet(), + newRelays = relays.toSet(), ) } diff --git a/app/src/main/java/com/greenart7c3/nostrsigner/ui/navigation/Route.kt b/app/src/main/java/com/greenart7c3/nostrsigner/ui/navigation/Route.kt index f245df01..6c841bce 100644 --- a/app/src/main/java/com/greenart7c3/nostrsigner/ui/navigation/Route.kt +++ b/app/src/main/java/com/greenart7c3/nostrsigner/ui/navigation/Route.kt @@ -81,13 +81,6 @@ sealed class Route( selectedIcon = Icons.Default.Settings, ) - data object NotificationType : Route( - title = NostrSigner.getInstance().getString(R.string.notification_type), - route = "NotificationType", - icon = Icons.Outlined.Settings, - selectedIcon = Icons.Default.Settings, - ) - data object DefaultRelays : Route( title = NostrSigner.getInstance().getString(R.string.default_relays), route = "DefaultRelays", @@ -182,7 +175,6 @@ val routes = listOf( Route.Logs, Route.ActiveRelays, Route.Language, - Route.NotificationType, Route.DefaultRelays, Route.SignPolicy, Route.Security, diff --git a/app/src/play/AndroidManifest.xml b/app/src/play/AndroidManifest.xml deleted file mode 100644 index 6d815c94..00000000 --- a/app/src/play/AndroidManifest.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/play/java/com/greenart7c3/nostrsigner/service/Notifications.kt b/app/src/play/java/com/greenart7c3/nostrsigner/service/Notifications.kt deleted file mode 100644 index 9b1b1b5a..00000000 --- a/app/src/play/java/com/greenart7c3/nostrsigner/service/Notifications.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.greenart7c3.nostrsigner.service - -import android.content.Intent -import android.util.Log -import android.util.LruCache -import com.google.firebase.messaging.FirebaseMessagingService -import com.google.firebase.messaging.RemoteMessage -import com.greenart7c3.nostrsigner.LocalPreferences -import com.greenart7c3.nostrsigner.service.NotificationUtils.getOrCreateDMChannel -import com.vitorpamplona.quartz.events.Event -import com.vitorpamplona.quartz.events.GiftWrapEvent -import kotlin.time.measureTimedValue -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel -import kotlinx.coroutines.launch - -class Notifications : FirebaseMessagingService() { - private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) - private val eventCache = LruCache(100) - - override fun getStartCommandIntent(originalIntent: Intent?): Intent { - return super.getStartCommandIntent(originalIntent) - } - - override fun handleIntent(intent: Intent?) { - Log.d("Time", "Intent received $intent") - super.handleIntent(intent) - } - - override fun onMessageReceived(remoteMessage: RemoteMessage) { - scope.launch(Dispatchers.IO) { - val (_, elapsed) = - measureTimedValue { parseMessage(remoteMessage.data)?.let { receiveIfNew(it) } } - Log.d("Time", "Notification processed in $elapsed") - } - super.onMessageReceived(remoteMessage) - } - - private fun parseMessage(params: Map): GiftWrapEvent? { - params["encryptedEvent"]?.let { eventStr -> - (Event.fromJson(eventStr) as? GiftWrapEvent)?.let { - return it - } - } - return null - } - - private suspend fun receiveIfNew(event: GiftWrapEvent) { - if (eventCache.get(event.id) == null) { - eventCache.put(event.id, event.id) - getOrCreateDMChannel(applicationContext) - EventNotificationConsumer(applicationContext).consume(event) - } - } - - override fun onNewToken(token: String) { - scope.launch(Dispatchers.IO) { - RegisterAccounts(LocalPreferences.allSavedAccounts(applicationContext)).go(token) - } - super.onNewToken(token) - } - - override fun onDestroy() { - Log.d("Lifetime Event", "PushNotificationReceiverService.onDestroy") - - scope.cancel() - super.onDestroy() - } -} diff --git a/app/src/play/java/com/greenart7c3/nostrsigner/service/PushNotificationUtils.kt b/app/src/play/java/com/greenart7c3/nostrsigner/service/PushNotificationUtils.kt deleted file mode 100644 index bb7cd105..00000000 --- a/app/src/play/java/com/greenart7c3/nostrsigner/service/PushNotificationUtils.kt +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2024 Vitor Pamplona - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the - * Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN - * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.greenart7c3.nostrsigner.service - -import android.util.Log -import com.google.firebase.messaging.FirebaseMessaging -import com.greenart7c3.nostrsigner.AccountInfo -import com.greenart7c3.nostrsigner.NostrSigner -import com.greenart7c3.nostrsigner.ui.AccountStateViewModel -import com.greenart7c3.nostrsigner.ui.NotificationType -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.tasks.await - -object PushNotificationUtils { - var hasInit: Boolean = false - var accountState: AccountStateViewModel? = null - - suspend fun init(accounts: List) = - with(Dispatchers.IO) { - if (hasInit || NostrSigner.getInstance().settings.notificationType == NotificationType.DIRECT) { - return@with - } - // get user notification token provided by firebase - try { - RegisterAccounts(accounts).go(FirebaseMessaging.getInstance().token.await()) - } catch (e: Exception) { - if (e is CancellationException) throw e - Log.e("Firebase token", "failed to get firebase token", e) - } - } -} diff --git a/app/src/play/java/com/greenart7c3/nostrsigner/ui/SelectNotificationProvider.kt b/app/src/play/java/com/greenart7c3/nostrsigner/ui/SelectNotificationProvider.kt deleted file mode 100644 index 76ff05b8..00000000 --- a/app/src/play/java/com/greenart7c3/nostrsigner/ui/SelectNotificationProvider.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.greenart7c3.nostrsigner.ui - -import androidx.compose.runtime.Composable - -@Composable -fun PushNotificationSettingsRow() {} diff --git a/build.gradle b/build.gradle index 406fbf1c..d24d34eb 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,6 @@ plugins { alias(libs.plugins.androidLibrary) apply false alias(libs.plugins.jetbrainsKotlinAndroid) apply false alias(libs.plugins.serialization) apply false - alias(libs.plugins.google.gms) version(libs.versions.gms) apply false alias(libs.plugins.kotlin.ksp) version(libs.versions.ksp) apply false alias(libs.plugins.gradle.ktlint) version(libs.versions.ktlint) apply false } diff --git a/build.sh b/build.sh index 6e3bede5..0ca2adb8 100755 --- a/build.sh +++ b/build.sh @@ -10,15 +10,13 @@ appName=$2 ./gradlew clean bundleRelease --stacktrace ./gradlew assembleRelease --stacktrace -rm ~/release/amber-* -rm ~/release/citrine-* -rm ~/release/manifest-* -mv app/build/outputs/bundle/playRelease/app-play-release.aab ~/release/ +rm -rf ~/release/amber-* +rm -rf ~/release/citrine-* +rm -rf ~/release/manifest-* mv app/build/outputs/bundle/offlineRelease/app-offline-release.aab ~/release/ mv app/build/outputs/bundle/freeRelease/app-free-release.aab ~/release/ mv app/build/outputs/apk/offline/release/app-offline-* ~/release/ mv app/build/outputs/apk/free/release/app-free-* ~/release/ -mv app/build/outputs/apk/play/release/app-play-* ~/release/ ./gradlew --stop cd ~/release ./generate_manifest.sh ${version} ${appName} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6e518b9e..07e2d5fa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,5 @@ [versions] activityCompose = "1.9.3" -unfiedpush = "2.4.0" appcompat = "1.7.0" biometricKtx = "1.2.0-alpha05" converterGson = "2.11.0" @@ -8,8 +7,6 @@ core = "3.5.3" coreKtx = "1.13.1" coreSplashscreen = "1.0.1" espressoCore = "3.6.1" -firebaseBom = "33.5.1" -firebaseMessaging = "24.0.3" jna = "5.14.0" junit = "4.13.2" junitVersion = "1.2.1" @@ -33,7 +30,6 @@ ksp = "2.0.20-1.0.24" [libraries] activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" } -unfiedpush = { module = "com.github.UnifiedPush:android-connector", version.ref = "unfiedpush" } appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } biometric-ktx = { module = "androidx.biometric:biometric-ktx", version.ref = "biometricKtx" } converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" } @@ -42,8 +38,6 @@ core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" } core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" } espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" } ext-junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" } -firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } -firebase-messaging = { module = "com.google.firebase:firebase-messaging", version.ref = "firebaseMessaging" } jna = { module = "net.java.dev.jna:jna", version.ref = "jna" } junit = { module = "junit:junit", version.ref = "junit" } lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle_version" } @@ -76,7 +70,6 @@ jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "k androidLibrary = { id = "com.android.library", version.ref = "agp" } androidApplication = { id = "com.android.application", version.ref = "agp" } gradle_ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "ktlint" } -google_gms = { id = "com.google.gms.google-services", version = "gms" } serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } kotlin_ksp = { id = "com.google.devtools.ksp", version = "ksp" } jetbrainsComposeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }