From 99b61f5331bbd55ce6f49b32f8f0b74acc084350 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Fri, 28 Mar 2025 15:54:24 +0100 Subject: [PATCH] test push notification Signed-off-by: sowjanyakch --- .../com/nextcloud/talk/api/NcApiCoroutines.kt | 7 + .../talk/dagger/modules/ViewModelModule.kt | 6 + .../talk/diagnose/DiagnoseActivity.kt | 18 +- .../diagnose/DiagnoseContentComposable.kt | 163 +++++++++++++++--- .../talk/diagnose/DiagnoseViewModel.kt | 59 +++++++ .../testNotification/TestNotificationData.kt | 24 +++ .../testNotification/TestNotificationOCS.kt | 26 +++ .../TestNotificationOverall.kt | 23 +++ .../java/com/nextcloud/talk/utils/ApiUtils.kt | 4 + app/src/main/res/values/strings.xml | 1 + 10 files changed, 305 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseViewModel.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationData.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationOCS.kt create mode 100644 app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationOverall.kt diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt index 08cc2f469..834c575ee 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt +++ b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt @@ -17,6 +17,7 @@ import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.participants.AddParticipantOverall import com.nextcloud.talk.models.json.participants.TalkBan import com.nextcloud.talk.models.json.participants.TalkBanOverall +import com.nextcloud.talk.models.json.testNotification.TestNotificationOverall import com.nextcloud.talk.models.json.userAbsence.UserAbsenceOverall import okhttp3.MultipartBody import okhttp3.RequestBody @@ -238,6 +239,12 @@ interface NcApiCoroutines { @Url url: String ): UserAbsenceOverall + @POST + suspend fun testPushNotifications( + @Header("Authorization") authorization: String, + @Url url: String + ): TestNotificationOverall + @GET suspend fun getContextOfChatMessage( @Header("Authorization") authorization: String, diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt index a5626faf2..a514d5bbc 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt @@ -16,6 +16,7 @@ import com.nextcloud.talk.conversationcreation.ConversationCreationViewModel import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel import com.nextcloud.talk.conversationinfoedit.viewmodel.ConversationInfoEditViewModel import com.nextcloud.talk.conversationlist.viewmodels.ConversationsListViewModel +import com.nextcloud.talk.diagnose.DiagnoseViewModel import com.nextcloud.talk.invitation.viewmodels.InvitationsViewModel import com.nextcloud.talk.messagesearch.MessageSearchViewModel import com.nextcloud.talk.openconversations.viewmodels.OpenConversationsViewModel @@ -148,4 +149,9 @@ abstract class ViewModelModule { @IntoMap @ViewModelKey(ConversationCreationViewModel::class) abstract fun conversationCreationViewModel(viewModel: ConversationCreationViewModel): ViewModel + + @Binds + @IntoMap + @ViewModelKey(DiagnoseViewModel::class) + abstract fun diagnoseViewModel(viewModel: DiagnoseViewModel): ViewModel } diff --git a/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseActivity.kt b/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseActivity.kt index 191507ac1..67bc22653 100644 --- a/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseActivity.kt @@ -10,6 +10,7 @@ import android.annotation.SuppressLint import android.content.ClipData import android.content.ClipboardManager import android.content.Intent +import android.net.Uri import android.os.Build import android.os.Build.MANUFACTURER import android.os.Build.MODEL @@ -27,6 +28,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource +import androidx.lifecycle.ViewModelProvider import androidx.core.net.toUri import autodagger.AutoInjector import com.nextcloud.talk.BuildConfig @@ -35,8 +37,8 @@ import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager -import com.nextcloud.talk.components.SetupSystemBars import com.nextcloud.talk.components.StandardAppBar +import com.nextcloud.talk.components.SetupSystemBars import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.BrandingUtils import com.nextcloud.talk.utils.ClosedInterfaceImpl @@ -56,6 +58,9 @@ class DiagnoseActivity : BaseActivity() { @Inject lateinit var arbitraryStorageManager: ArbitraryStorageManager + @Inject + lateinit var viewModelFactory: ViewModelProvider.Factory + @Inject lateinit var ncApi: NcApi @@ -78,6 +83,10 @@ class DiagnoseActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + val diagnoseViewModel = ViewModelProvider( + this, + viewModelFactory + )[DiagnoseViewModel::class.java] val colorScheme = viewThemeUtils.getColorScheme(this) @@ -113,7 +122,7 @@ class DiagnoseActivity : BaseActivity() { .background(backgroundColor) .fillMaxSize() ) { - DiagnoseContentComposable(diagnoseDataState) + DiagnoseContentComposable(diagnoseDataState, diagnoseViewModel) } } ) @@ -132,6 +141,7 @@ class DiagnoseActivity : BaseActivity() { setupMetaValues() setupPhoneValues() setupAppValues() + testPushNotification() setupAccountValues() diagnoseDataState.value = diagnoseData.toList() @@ -187,6 +197,10 @@ class DiagnoseActivity : BaseActivity() { ).show() } + private fun testPushNotification() { + addHeadline(context.resources.getString(R.string.nc_test_push_button)) + } + private fun setupMetaValues() { addHeadline(context.resources.getString(R.string.nc_diagnose_meta_category_title)) addDiagnosisEntry( diff --git a/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseContentComposable.kt b/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseContentComposable.kt index 7a3737af5..65d2deac4 100644 --- a/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseContentComposable.kt +++ b/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseContentComposable.kt @@ -7,28 +7,53 @@ package com.nextcloud.talk.diagnose +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.widget.Toast +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties import com.nextcloud.talk.R @Composable -fun DiagnoseContentComposable(data: State>) { +fun DiagnoseContentComposable( + data: State>, + diagnoseViewModel: DiagnoseViewModel +) { + val context = LocalContext.current + val message = diagnoseViewModel.notificationMessage + val isLoading = diagnoseViewModel.isLoading + val showDialog = diagnoseViewModel.showDialog Column( modifier = Modifier .fillMaxSize() @@ -37,13 +62,33 @@ fun DiagnoseContentComposable(data: State ) { data.value.forEach { element -> when (element) { - is DiagnoseActivity.DiagnoseElement.DiagnoseHeadline -> Text( - modifier = Modifier.padding(vertical = 16.dp), - text = element.headline, - color = MaterialTheme.colorScheme.primary, - fontSize = LocalDensity.current.run { dimensionResource(R.dimen.headline_text_size).toPx().toSp() }, - fontWeight = FontWeight.Bold - ) + is DiagnoseActivity.DiagnoseElement.DiagnoseHeadline -> { + if (element.headline == "Test push notifications") { + Text( + text = element.headline, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp) + .clickable { diagnoseViewModel.fetchTestPushResult() }, + color = MaterialTheme.colorScheme.secondary, + fontSize = LocalDensity.current.run { + dimensionResource(R.dimen.headline_text_size).toPx().toSp() + }, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Start + ) + } else { + Text( + modifier = Modifier.fillMaxWidth().padding(vertical = 16.dp), + text = element.headline, + color = MaterialTheme.colorScheme.primary, + fontSize = LocalDensity.current.run { + dimensionResource(R.dimen.headline_text_size).toPx().toSp() + }, + fontWeight = FontWeight.Bold + ) + } + } is DiagnoseActivity.DiagnoseElement.DiagnoseEntry -> { Text( @@ -59,19 +104,89 @@ fun DiagnoseContentComposable(data: State } } } + if (isLoading.value) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } + + if (showDialog.value) { + Dialog( + onDismissRequest = { diagnoseViewModel.dismissDialog() }, + properties = DialogProperties( + dismissOnClickOutside = true, + usePlatformDefaultWidth = false + ) + ) { + if (showDialog.value) { + Dialog( + onDismissRequest = { diagnoseViewModel.dismissDialog() }, + properties = DialogProperties( + dismissOnClickOutside = true, + usePlatformDefaultWidth = false + ) + ) { + Surface( + shape = MaterialTheme.shapes.medium, + tonalElevation = 8.dp, + modifier = Modifier + .wrapContentSize() + .padding(16.dp) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text("Push Test Result", style = MaterialTheme.typography.titleMedium) + Spacer(modifier = Modifier.height(12.dp)) + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f, fill = false) + .verticalScroll(rememberScrollState()) + ) { + Text(message.value) + } + Spacer(modifier = Modifier.height(16.dp)) + Row( + horizontalArrangement = Arrangement.SpaceEvenly, + modifier = Modifier.fillMaxWidth() + ) { + TextButton(onClick = { diagnoseViewModel.dismissDialog() }) { + Text("Cancel") + } + Spacer(modifier = Modifier.width(8.dp)) + TextButton(onClick = { + val clipboard = + context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + val clip = ClipData.newPlainText("PushMessage", message.value) + clipboard.setPrimaryClip(clip) + Toast.makeText(context, "Message copied to clipboard", Toast.LENGTH_SHORT) + .show() + diagnoseViewModel.dismissDialog() + }) { + Text("Copy") + } + } + } + } + } + } + } + } } } -@Preview(showBackground = true) -@Composable -fun DiagnoseContentPreview() { - val state = remember { - mutableStateOf( - listOf( - DiagnoseActivity.DiagnoseElement.DiagnoseHeadline("Headline"), - DiagnoseActivity.DiagnoseElement.DiagnoseEntry("Key", "Value") - ) - ) - } - DiagnoseContentComposable(state) -} +// @Preview(showBackground = true) +// @Composable +// fun DiagnoseContentPreview() { +// val state = remember { +// mutableStateOf( +// listOf( +// DiagnoseActivity.DiagnoseElement.DiagnoseHeadline("Headline"), +// DiagnoseActivity.DiagnoseElement.DiagnoseEntry("Key", "Value") +// ) +// ) +// } +// DiagnoseContentComposable(state) +// } diff --git a/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseViewModel.kt b/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseViewModel.kt new file mode 100644 index 000000000..d54e7667d --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseViewModel.kt @@ -0,0 +1,59 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.diagnose + +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.nextcloud.talk.api.NcApiCoroutines +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew +import kotlinx.coroutines.launch +import javax.inject.Inject + +class DiagnoseViewModel @Inject constructor( + private val ncApiCoroutines: NcApiCoroutines, + private val currentUserProvider: CurrentUserProviderNew +) : ViewModel() { + private val _currentUser = currentUserProvider.currentUser.blockingGet() + val currentUser: User = _currentUser + val credentials = ApiUtils.getCredentials(_currentUser.username, _currentUser.token) ?: "" + + private val _notificationMessage = mutableStateOf("") + val notificationMessage = _notificationMessage + + private val _isLoading = mutableStateOf(false) + val isLoading = _isLoading + + private val _showDialog = mutableStateOf(false) + val showDialog = _showDialog + + fun fetchTestPushResult() { + viewModelScope.launch { + try { + _isLoading.value = true + val response = ncApiCoroutines.testPushNotifications( + credentials, + ApiUtils + .getUrlForTestPushNotifications(_currentUser.baseUrl ?: "") + ) + _notificationMessage.value = response.ocs?.data?.message ?: "Error while fetching test push message" + } catch (e: Exception) { + _notificationMessage.value = "Exception: ${e.localizedMessage}" + } finally { + _isLoading.value = false + _showDialog.value = true + } + } + } + + fun dismissDialog() { + _showDialog.value = false + } +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationData.kt b/app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationData.kt new file mode 100644 index 000000000..6eea7d48f --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationData.kt @@ -0,0 +1,24 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.models.json.testNotification + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +data class TestNotificationData( + @JsonField(name = ["message"]) + var message: String +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : + this("") +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationOCS.kt b/app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationOCS.kt new file mode 100644 index 000000000..86ce3245d --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationOCS.kt @@ -0,0 +1,26 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.models.json.testNotification + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import com.nextcloud.talk.models.json.generic.GenericMeta +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +data class TestNotificationOCS( + @JsonField(name = ["meta"]) + var meta: GenericMeta?, + @JsonField(name = ["data"]) + var data: TestNotificationData? +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null, null) +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationOverall.kt b/app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationOverall.kt new file mode 100644 index 000000000..858b084a6 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/testNotification/TestNotificationOverall.kt @@ -0,0 +1,23 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2025 Your Name + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.nextcloud.talk.models.json.testNotification + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +data class TestNotificationOverall( + @JsonField(name = ["ocs"]) + var ocs: TestNotificationOCS? +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null) +} diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt index 8dc117490..875e7bb59 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt @@ -273,6 +273,10 @@ object ApiUtils { return getUrlForApi(version, baseUrl) + "/signaling" } + fun getUrlForTestPushNotifications(baseUrl: String): String { + return "$baseUrl$OCS_API_VERSION/apps/notifications/api/v3/test/self" + } + @JvmStatic fun getUrlForSignalingBackend(version: Int, baseUrl: String?): String { return getUrlForSignaling(version, baseUrl) + "/backend" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4742ce754..0495dee53 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -228,6 +228,7 @@ How to translate with transifex: Send email Create issue Build flavor + "Test push notifications Leave conversation