From 60a52ba60ddfa297a164bbb0330cb08edd71b8b1 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 3 Sep 2026 14:27:00 +0200 Subject: [PATCH] feat(diagnosis): add "Share diagnosis report" to Diagnosis and Logs menus Extract the share/download/create-issue dialog from SettingsActivity into a shared ShareDiagnosisReportDialog so DiagnosisActivity and LogsActivity can open the same dialog from their overflow menu instead of only being reachable via Settings. Assisted-by: Claude Code:claude-sonnet-5 Signed-off-by: Marcel Hibbe --- .../talk/diagnosis/DiagnosisActivity.kt | 148 ++++++++++----- .../diagnosis/ShareDiagnosisReportDialog.kt | 177 ++++++++++++++++++ .../nextcloud/talk/logger/ui/LogsActivity.kt | 38 +++- .../talk/settings/SettingsActivity.kt | 125 +------------ 4 files changed, 316 insertions(+), 172 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/diagnosis/ShareDiagnosisReportDialog.kt diff --git a/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt b/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt index aeef186e0b3..ebd4d0c0ed2 100644 --- a/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt @@ -11,14 +11,18 @@ import android.content.ClipboardManager import android.os.Bundle import android.widget.Toast import androidx.activity.compose.setContent +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.displayCutoutPadding import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.material3.ColorScheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier @@ -26,6 +30,7 @@ import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import autodagger.AutoInjector import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity @@ -35,11 +40,14 @@ import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager import com.nextcloud.talk.components.ColoredStatusBar import com.nextcloud.talk.components.StandardAppBar import com.nextcloud.talk.data.network.NetworkMonitor +import com.nextcloud.talk.errorhandling.saveLogsAsZip import com.nextcloud.talk.logger.LogsRepository import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ClosedInterfaceImpl import com.nextcloud.talk.utils.UnifiedPushUtils import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -69,6 +77,18 @@ class DiagnosisActivity : BaseActivity() { private val diagnosisData = mutableListOf() private val diagnosisDataState = mutableStateOf(emptyList()) + private val saveZipLauncher = + registerForActivityResult(ActivityResultContracts.CreateDocument("application/zip")) { uri -> + if (uri != null) { + val diagnosisText = buildDiagnosisReportText(this, userManager, appPreferences, logsRepository) + lifecycleScope.launch(Dispatchers.IO) { + contentResolver.openOutputStream(uri)?.use { os -> + saveLogsAsZip(this@DiagnosisActivity, os, diagnosisText) + } + } + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) @@ -81,59 +101,19 @@ class DiagnosisActivity : BaseActivity() { val isGooglePlayServicesAvailable = ClosedInterfaceImpl().isGooglePlayServicesAvailable val useUnifiedPush = appPreferences.useUnifiedPush val useEmbeddedDistrib = UnifiedPushUtils.hasEmbeddedDistributor(context) && !useUnifiedPush + val showTestPushButton = isGooglePlayServicesAvailable || useUnifiedPush || useEmbeddedDistrib setContent { - val backgroundColor = colorResource(id = R.color.bg_default) - - val menuItems = listOf( - stringResource(R.string.nc_common_copy) to { copyToClipboard(diagnosisData.toMarkdown()) } + DiagnosisScreen( + colorScheme = colorScheme, + networkMonitor = networkMonitor, + diagnosisData = diagnosisData, + diagnosisDataState = diagnosisDataState, + diagnosisViewModel = diagnosisViewModel, + showTestPushButton = showTestPushButton, + onCopyClick = ::copyToClipboard, + onShareReportClick = ::openShareReportDialog ) - - MaterialTheme( - colorScheme = colorScheme - ) { - val isOnline = networkMonitor.isOnline.collectAsState().value - ColoredStatusBar() - Scaffold( - modifier = Modifier - .statusBarsPadding() - .displayCutoutPadding(), - topBar = { - StandardAppBar( - title = stringResource(R.string.nc_settings_diagnosis_title), - menuItems - ) - }, - content = { paddingValues -> - val viewState = diagnosisViewModel.notificationViewState.collectAsState().value - - Column( - Modifier - .background(backgroundColor) - .padding( - 0.dp, - paddingValues.calculateTopPadding(), - 0.dp, - paddingValues.calculateBottomPadding() - ) - .fillMaxSize() - ) { - DiagnosisContentComposable( - diagnosisDataState, - isLoading = diagnosisViewModel.isLoading.value, - showDialog = diagnosisViewModel.showDialog.value, - viewState = viewState, - onTestPushClick = { diagnosisViewModel.fetchTestPushResult() }, - onDismissDialog = { diagnosisViewModel.dismissDialog() }, - showTestPushButton = isGooglePlayServicesAvailable || - useUnifiedPush || - useEmbeddedDistrib, - isOnline = isOnline - ) - } - } - ) - } } } @@ -154,6 +134,10 @@ class DiagnosisActivity : BaseActivity() { diagnosisDataState.value = diagnosisData.toList() } + private fun openShareReportDialog() { + showShareReportDialog(this, userManager, appPreferences, logsRepository, saveZipLauncher) + } + private fun copyToClipboard(text: String) { val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager @@ -174,3 +158,67 @@ class DiagnosisActivity : BaseActivity() { val TAG = DiagnosisActivity::class.java.simpleName } } + +@Suppress("LongParameterList") +@Composable +private fun DiagnosisScreen( + colorScheme: ColorScheme, + networkMonitor: NetworkMonitor, + diagnosisData: List, + diagnosisDataState: MutableState>, + diagnosisViewModel: DiagnosisViewModel, + showTestPushButton: Boolean, + onCopyClick: (String) -> Unit, + onShareReportClick: () -> Unit +) { + val backgroundColor = colorResource(id = R.color.bg_default) + + val menuItems = listOf( + stringResource(R.string.nc_common_copy) to { onCopyClick(diagnosisData.toMarkdown()) }, + stringResource(R.string.nc_settings_share_report_title) to onShareReportClick + ) + + MaterialTheme( + colorScheme = colorScheme + ) { + val isOnline = networkMonitor.isOnline.collectAsState().value + ColoredStatusBar() + Scaffold( + modifier = Modifier + .statusBarsPadding() + .displayCutoutPadding(), + topBar = { + StandardAppBar( + title = stringResource(R.string.nc_settings_diagnosis_title), + menuItems + ) + }, + content = { paddingValues -> + val viewState = diagnosisViewModel.notificationViewState.collectAsState().value + + Column( + Modifier + .background(backgroundColor) + .padding( + 0.dp, + paddingValues.calculateTopPadding(), + 0.dp, + paddingValues.calculateBottomPadding() + ) + .fillMaxSize() + ) { + DiagnosisContentComposable( + diagnosisDataState, + isLoading = diagnosisViewModel.isLoading.value, + showDialog = diagnosisViewModel.showDialog.value, + viewState = viewState, + onTestPushClick = { diagnosisViewModel.fetchTestPushResult() }, + onDismissDialog = { diagnosisViewModel.dismissDialog() }, + showTestPushButton = showTestPushButton, + isOnline = isOnline + ) + } + } + ) + } +} diff --git a/app/src/main/java/com/nextcloud/talk/diagnosis/ShareDiagnosisReportDialog.kt b/app/src/main/java/com/nextcloud/talk/diagnosis/ShareDiagnosisReportDialog.kt new file mode 100644 index 00000000000..721e5c11f71 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/diagnosis/ShareDiagnosisReportDialog.kt @@ -0,0 +1,177 @@ +/* + * Nextcloud Talk - Android Client + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package com.nextcloud.talk.diagnosis + +import android.app.Activity +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context.CLIPBOARD_SERVICE +import android.content.Intent +import android.util.TypedValue +import android.view.View +import android.widget.LinearLayout +import android.widget.ScrollView +import android.widget.TextView +import android.widget.Toast +import androidx.activity.result.ActivityResultLauncher +import androidx.appcompat.app.AlertDialog +import androidx.core.net.toUri +import com.google.android.material.color.MaterialColors +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.nextcloud.talk.R +import com.nextcloud.talk.errorhandling.shareLogsAndDiagnosis +import com.nextcloud.talk.logger.LogsRepository +import com.nextcloud.talk.users.UserManager +import com.nextcloud.talk.utils.BrandingUtils +import com.nextcloud.talk.utils.preferences.AppPreferences + +private const val DIALOG_PADDING_H_DP = 24 +private const val DIALOG_PADDING_V_DP = 16 +private const val DIALOG_SPACING_DP = 8 + +fun showShareReportDialog( + activity: Activity, + userManager: UserManager, + appPreferences: AppPreferences, + logsRepository: LogsRepository, + saveZipLauncher: ActivityResultLauncher +) { + val options = buildShareReportOptions(activity, userManager, appPreferences, logsRepository, saveZipLauncher) + var dialog: AlertDialog? = null + val view = buildShareDialogContentView(activity, options) { dialog?.dismiss() } + dialog = MaterialAlertDialogBuilder(activity) + .setTitle(R.string.nc_settings_share_report_title) + .setView(view) + .show() +} + +private fun buildShareReportOptions( + activity: Activity, + userManager: UserManager, + appPreferences: AppPreferences, + logsRepository: LogsRepository, + saveZipLauncher: ActivityResultLauncher +): List Unit>> { + val options = mutableListOf( + activity.getString(R.string.nc_logs_share) to { + val diagnosisText = buildDiagnosisReportText(activity, userManager, appPreferences, logsRepository) + shareLogsAndDiagnosis( + context = activity, + subject = activity.getString( + R.string.nc_logs_share_subject, + activity.getString(R.string.nc_app_product_name) + ), + diagnosisText = diagnosisText + ) + }, + activity.getString(R.string.nc_logs_download_zip) to { + saveZipLauncher.launch("nc_talk_logs.zip") + } + ) + if (BrandingUtils.isOriginalNextcloudClient(activity.applicationContext)) { + options.add( + activity.getString(R.string.create_issue) to { + val diagnosisText = buildDiagnosisReportText(activity, userManager, appPreferences, logsRepository) + val clipboard = activity.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager + clipboard.setPrimaryClip( + ClipData.newPlainText(activity.getString(R.string.nc_app_product_name), diagnosisText) + ) + Toast.makeText( + activity, + activity.getString(R.string.nc_common_copy_success), + Toast.LENGTH_LONG + ).show() + activity.startActivity( + Intent(Intent.ACTION_VIEW, activity.getString(R.string.nc_talk_android_issues_url).toUri()) + ) + } + ) + } + return options +} + +@Suppress("Detekt.LongMethod") +fun buildDiagnosisReportText( + activity: Activity, + userManager: UserManager, + appPreferences: AppPreferences, + logsRepository: LogsRepository +): String = + buildDiagnosisElements( + context = activity, + userManager = userManager, + appPreferences = appPreferences, + logsRepository = logsRepository + ).toMarkdown() + +@Suppress("LongMethod") +private fun buildShareDialogContentView( + activity: Activity, + options: List Unit>>, + onDismiss: () -> Unit +): ScrollView { + val density = activity.resources.displayMetrics.density + fun dp(value: Int) = (value * density).toInt() + val selectableBackground = with(TypedValue()) { + activity.theme.resolveAttribute(android.R.attr.selectableItemBackground, this, true) + resourceId + } + val list = LinearLayout(activity).apply { + orientation = LinearLayout.VERTICAL + addView( + TextView(activity).apply { + text = activity.getString(R.string.nc_logs_advanced_logging_privacy_warning) + val h = dp(DIALOG_PADDING_H_DP) + val v = dp(DIALOG_PADDING_V_DP) + setPadding(h, v, h, v) + setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodyMedium) + } + ) + addView( + View(activity).apply { + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + dp(1) + ).also { it.setMargins(0, 0, 0, dp(DIALOG_SPACING_DP)) } + setBackgroundColor( + MaterialColors.getColor( + activity, + com.google.android.material.R.attr.colorOutlineVariant, + 0 + ) + ) + } + ) + options.forEach { (label, action) -> + addView( + TextView(activity).apply { + text = label + val h = dp(DIALOG_PADDING_H_DP) + val v = dp(DIALOG_PADDING_V_DP) + setPadding(h, v, h, v) + setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodyLarge) + setBackgroundResource(selectableBackground) + isClickable = true + isFocusable = true + setOnClickListener { + onDismiss() + action() + } + } + ) + } + addView( + View(activity).apply { + layoutParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + dp(DIALOG_SPACING_DP) + ) + } + ) + } + return ScrollView(activity).apply { addView(list) } +} diff --git a/app/src/main/java/com/nextcloud/talk/logger/ui/LogsActivity.kt b/app/src/main/java/com/nextcloud/talk/logger/ui/LogsActivity.kt index 3b8cb511a0b..827a9a67e86 100644 --- a/app/src/main/java/com/nextcloud/talk/logger/ui/LogsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/logger/ui/LogsActivity.kt @@ -8,6 +8,7 @@ package com.nextcloud.talk.logger.ui import android.os.Bundle import androidx.activity.compose.setContent +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll @@ -52,14 +53,22 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope import autodagger.AutoInjector import com.nextcloud.talk.R import com.nextcloud.talk.activities.BaseActivity import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.components.ColoredStatusBar import com.nextcloud.talk.components.StandardAppBar +import com.nextcloud.talk.diagnosis.buildDiagnosisReportText +import com.nextcloud.talk.diagnosis.showShareReportDialog +import com.nextcloud.talk.errorhandling.saveLogsAsZip import com.nextcloud.talk.logger.Level import com.nextcloud.talk.logger.LogEntry +import com.nextcloud.talk.logger.LogsRepository +import com.nextcloud.talk.users.UserManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import java.text.SimpleDateFormat import java.util.Locale import javax.inject.Inject @@ -70,6 +79,24 @@ class LogsActivity : BaseActivity() { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory + @Inject + lateinit var userManager: UserManager + + @Inject + lateinit var logsRepository: LogsRepository + + private val saveZipLauncher = + registerForActivityResult(ActivityResultContracts.CreateDocument("application/zip")) { uri -> + if (uri != null) { + val diagnosisText = buildDiagnosisReportText(this, userManager, appPreferences, logsRepository) + lifecycleScope.launch(Dispatchers.IO) { + contentResolver.openOutputStream(uri)?.use { os -> + saveLogsAsZip(this@LogsActivity, os, diagnosisText) + } + } + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) @@ -86,7 +113,16 @@ class LogsActivity : BaseActivity() { val advancedLogging = viewModel.advancedLogging.collectAsState().value val menuItems = listOf( - stringResource(R.string.nc_logs_delete_all) to { viewModel.deleteAll() } + stringResource(R.string.nc_logs_delete_all) to { viewModel.deleteAll() }, + stringResource(R.string.nc_settings_share_report_title) to { + showShareReportDialog( + this@LogsActivity, + userManager, + appPreferences, + logsRepository, + saveZipLauncher + ) + } ) ColoredStatusBar() diff --git a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt index c07c167f70b..d93fa5fd4b7 100644 --- a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt @@ -65,10 +65,9 @@ import com.nextcloud.talk.data.network.NetworkMonitor import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.ActivitySettingsBinding import com.nextcloud.talk.diagnosis.DiagnosisActivity -import com.nextcloud.talk.diagnosis.buildDiagnosisElements -import com.nextcloud.talk.diagnosis.toMarkdown +import com.nextcloud.talk.diagnosis.buildDiagnosisReportText +import com.nextcloud.talk.diagnosis.showShareReportDialog import com.nextcloud.talk.errorhandling.saveLogsAsZip -import com.nextcloud.talk.errorhandling.shareLogsAndDiagnosis import com.nextcloud.talk.logger.Level import com.nextcloud.talk.logger.LogsRepository import com.nextcloud.talk.logger.ui.LogsActivity @@ -83,7 +82,6 @@ import com.nextcloud.talk.profile.ProfileActivity import com.nextcloud.talk.ui.dialog.SetPhoneNumberDialogFragment import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils -import com.nextcloud.talk.utils.BrandingUtils import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.ClosedInterfaceImpl import com.nextcloud.talk.utils.DisplayUtils @@ -147,7 +145,7 @@ class SettingsActivity : private val saveZipLauncher = registerForActivityResult(ActivityResultContracts.CreateDocument("application/zip")) { uri -> if (uri != null) { - val diagnosisText = buildDiagnosisReport() + val diagnosisText = buildDiagnosisReportText(this, userManager, appPreferences, logsRepository) lifecycleScope.launch(Dispatchers.IO) { contentResolver.openOutputStream(uri)?.use { os -> saveLogsAsZip(this@SettingsActivity, os, diagnosisText) @@ -699,125 +697,10 @@ class SettingsActivity : startActivity(Intent(context, LogsActivity::class.java)) } binding.shareReportWrapper.setOnClickListener { - showShareReportDialog() + showShareReportDialog(this, userManager, appPreferences, logsRepository, saveZipLauncher) } } - private fun showShareReportDialog() { - val options = buildShareReportOptions() - var dialog: AlertDialog? = null - val view = buildShareDialogContentView(options) { dialog?.dismiss() } - dialog = MaterialAlertDialogBuilder(this) - .setTitle(R.string.nc_settings_share_report_title) - .setView(view) - .show() - } - - private fun buildShareReportOptions(): List Unit>> { - val options = mutableListOf( - getString(R.string.nc_logs_share) to { - val diagnosisText = buildDiagnosisReport() - shareLogsAndDiagnosis( - context = this, - subject = getString(R.string.nc_logs_share_subject, getString(R.string.nc_app_product_name)), - diagnosisText = diagnosisText - ) - }, - getString(R.string.nc_logs_download_zip) to { - saveZipLauncher.launch("nc_talk_logs.zip") - } - ) - if (BrandingUtils.isOriginalNextcloudClient(applicationContext)) { - options.add( - getString(R.string.create_issue) to { - val diagnosisText = buildDiagnosisReport() - val clipboard = getSystemService(CLIPBOARD_SERVICE) as android.content.ClipboardManager - clipboard.setPrimaryClip( - android.content.ClipData.newPlainText(getString(R.string.nc_app_product_name), diagnosisText) - ) - Toast.makeText(this, getString(R.string.nc_common_copy_success), Toast.LENGTH_LONG).show() - startActivity(Intent(Intent.ACTION_VIEW, getString(R.string.nc_talk_android_issues_url).toUri())) - } - ) - } - return options - } - - @Suppress("Detekt.LongMethod") - private fun buildDiagnosisReport(): String = - buildDiagnosisElements( - context = this, - userManager = userManager, - appPreferences = appPreferences, - logsRepository = logsRepository - ).toMarkdown() - - private fun buildShareDialogContentView( - options: List Unit>>, - onDismiss: () -> Unit - ): android.widget.ScrollView { - val density = resources.displayMetrics.density - fun dp(value: Int) = (value * density).toInt() - val selectableBackground = with(android.util.TypedValue()) { - theme.resolveAttribute(android.R.attr.selectableItemBackground, this, true) - resourceId - } - val list = android.widget.LinearLayout(this).apply { - orientation = android.widget.LinearLayout.VERTICAL - addView( - android.widget.TextView(context).apply { - text = getString(R.string.nc_logs_advanced_logging_privacy_warning) - val h = dp(DIALOG_PADDING_H_DP) - val v = dp(DIALOG_PADDING_V_DP) - setPadding(h, v, h, v) - setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodyMedium) - } - ) - addView( - android.view.View(context).apply { - layoutParams = android.widget.LinearLayout.LayoutParams( - android.widget.LinearLayout.LayoutParams.MATCH_PARENT, - dp(1) - ).also { it.setMargins(0, 0, 0, dp(DIALOG_SPACING_DP)) } - setBackgroundColor( - com.google.android.material.color.MaterialColors.getColor( - context, - com.google.android.material.R.attr.colorOutlineVariant, - 0 - ) - ) - } - ) - options.forEach { (label, action) -> - addView( - android.widget.TextView(context).apply { - text = label - val h = dp(DIALOG_PADDING_H_DP) - val v = dp(DIALOG_PADDING_V_DP) - setPadding(h, v, h, v) - setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodyLarge) - setBackgroundResource(selectableBackground) - isClickable = true - isFocusable = true - setOnClickListener { - onDismiss() - action() - } - } - ) - } - addView( - View(context).apply { - layoutParams = android.widget.LinearLayout.LayoutParams( - android.widget.LinearLayout.LayoutParams.MATCH_PARENT, - dp(DIALOG_SPACING_DP) - ) - } - ) - } - return android.widget.ScrollView(this).apply { addView(list) } - } - private fun setupPrivacyUrl(isOnline: Boolean) { if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_privacy_url)) && isOnline) { binding.settingsPrivacy.visibility = View.VISIBLE