Browse Source

Merge pull request #4476 from nextcloud/featue/noid/addIgnoreBatteryOptimizationHint

Add permanent warning when notifications are not set up correctly
Marcel Hibbe 7 months ago
parent
commit
f048dc9463

+ 53 - 7
app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt

@@ -110,6 +110,7 @@ import com.nextcloud.talk.utils.ClosedInterfaceImpl
 import com.nextcloud.talk.utils.ConversationUtils
 import com.nextcloud.talk.utils.FileUtils
 import com.nextcloud.talk.utils.Mimetype
+import com.nextcloud.talk.utils.NotificationUtils
 import com.nextcloud.talk.utils.ParticipantPermissions
 import com.nextcloud.talk.utils.SpreedFeatures
 import com.nextcloud.talk.utils.UserIdUtils
@@ -121,6 +122,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FORWARD_MSG_TEXT
 import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID
 import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NEW_CONVERSATION
 import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
+import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SCROLL_TO_NOTIFICATION_CATEGORY
 import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SHARED_TEXT
 import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
 import com.nextcloud.talk.utils.power.PowerManagerUtils
@@ -273,6 +275,8 @@ class ConversationsListActivity :
         adapter!!.addListener(this)
         prepareViews()
 
+        updateNotificationWarning()
+
         showShareToScreen = hasActivityActionSendIntent()
 
         if (!eventBus.isRegistered(this)) {
@@ -303,6 +307,14 @@ class ConversationsListActivity :
         showSearchOrToolbar()
     }
 
+    private fun updateNotificationWarning() {
+        if (shouldShowNotificationWarning()) {
+            showNotificationWarning()
+        } else {
+            binding.chatListNotificationWarning.visibility = View.GONE
+        }
+    }
+
     private fun initObservers() {
         this.lifecycleScope.launch {
             networkMonitor.isOnline.onEach { isOnline ->
@@ -763,9 +775,8 @@ class ConversationsListActivity :
         )
     }
 
-    private fun hasActivityActionSendIntent(): Boolean {
-        return Intent.ACTION_SEND == intent.action || Intent.ACTION_SEND_MULTIPLE == intent.action
-    }
+    private fun hasActivityActionSendIntent(): Boolean =
+        Intent.ACTION_SEND == intent.action || Intent.ACTION_SEND_MULTIPLE == intent.action
 
     private fun showSearchView(searchView: SearchView?, searchItem: MenuItem?) {
         binding.conversationListAppbar.stateListAnimator = AnimatorInflater.loadStateListAnimator(
@@ -1274,10 +1285,9 @@ class ConversationsListActivity :
             !participantPermissions.canIgnoreLobby()
     }
 
-    private fun isReadOnlyConversation(conversation: ConversationModel): Boolean {
-        return conversation.conversationReadOnlyState ===
+    private fun isReadOnlyConversation(conversation: ConversationModel): Boolean =
+        conversation.conversationReadOnlyState ===
             ConversationEnums.ConversationReadOnlyState.CONVERSATION_READ_ONLY
-    }
 
     private fun handleSharedData() {
         collectDataFromIntent()
@@ -1453,8 +1463,9 @@ class ConversationsListActivity :
             }
 
             REQUEST_POST_NOTIFICATIONS_PERMISSION -> {
-                // whenever user allowed notifications, also check to ignore battery optimization
                 if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    Log.d(TAG, "Notification permission was granted")
+
                     if (!PowerManagerUtils().isIgnoringBatteryOptimizations() &&
                         ClosedInterfaceImpl().isGooglePlayServicesAvailable
                     ) {
@@ -1490,6 +1501,41 @@ class ConversationsListActivity :
         }
     }
 
+    private fun showNotificationWarning() {
+        binding.chatListNotificationWarning.visibility = View.VISIBLE
+        binding.chatListNotificationWarning.setOnClickListener {
+            val bundle = Bundle()
+            bundle.putBoolean(KEY_SCROLL_TO_NOTIFICATION_CATEGORY, true)
+            val settingsIntent = Intent(context, SettingsActivity::class.java)
+            settingsIntent.putExtras(bundle)
+            startActivity(settingsIntent)
+        }
+    }
+
+    private fun shouldShowNotificationWarning(): Boolean {
+        val notificationPermissionNotGranted = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
+            !platformPermissionUtil.isPostNotificationsPermissionGranted()
+        val batteryOptimizationNotIgnored = !PowerManagerUtils().isIgnoringBatteryOptimizations()
+
+        val messagesChannelNotEnabled = !NotificationUtils.isMessagesNotificationChannelEnabled(this)
+        val callsChannelNotEnabled = !NotificationUtils.isCallsNotificationChannelEnabled(this)
+
+        val serverNotificationAppInstalled =
+            userManager.currentUser.blockingGet().capabilities?.notificationsCapability?.features?.isNotEmpty() ?: false
+
+        val settingsOfUserAreWrong = notificationPermissionNotGranted ||
+            batteryOptimizationNotIgnored ||
+            messagesChannelNotEnabled ||
+            callsChannelNotEnabled ||
+            !serverNotificationAppInstalled
+
+        val userWantsToBeNotifiedAboutWrongSettings = appPreferences.getShowNotificationWarning()
+
+        return settingsOfUserAreWrong &&
+            userWantsToBeNotifiedAboutWrongSettings &&
+            ClosedInterfaceImpl().isGooglePlayServicesAvailable
+    }
+
     private fun openConversation(textToPaste: String? = "") {
         if (CallActivity.active &&
             selectedConversation!!.token != ApplicationWideCurrentRoomHolder.getInstance().currentRoomToken

+ 98 - 17
app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt

@@ -71,12 +71,14 @@ import com.nextcloud.talk.utils.ApiUtils
 import com.nextcloud.talk.utils.CapabilitiesUtil
 import com.nextcloud.talk.utils.ClosedInterfaceImpl
 import com.nextcloud.talk.utils.DisplayUtils
+import com.nextcloud.talk.utils.DrawableUtils
 import com.nextcloud.talk.utils.LoggingUtils.sendMailWithAttachment
 import com.nextcloud.talk.utils.NotificationUtils
 import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri
 import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri
 import com.nextcloud.talk.utils.SecurityUtils
 import com.nextcloud.talk.utils.SpreedFeatures
+import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SCROLL_TO_NOTIFICATION_CATEGORY
 import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
 import com.nextcloud.talk.utils.power.PowerManagerUtils
 import com.nextcloud.talk.utils.preferences.AppPreferencesImpl
@@ -101,7 +103,9 @@ import javax.inject.Inject
 
 @Suppress("LargeClass", "TooManyFunctions")
 @AutoInjector(NextcloudTalkApplication::class)
-class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNumberDialogClickListener {
+class SettingsActivity :
+    BaseActivity(),
+    SetPhoneNumberDialogFragment.SetPhoneNumberDialogClickListener {
     private lateinit var binding: ActivitySettingsBinding
 
     @Inject
@@ -129,6 +133,7 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
     private lateinit var phoneBookIntegrationFlow: Flow<Boolean>
     private var profileQueryDisposable: Disposable? = null
     private var dbQueryDisposable: Disposable? = null
+    private var openedByNotificationWarning: Boolean = false
 
     @SuppressLint("StringFormatInvalid")
     override fun onCreate(savedInstanceState: Bundle?) {
@@ -143,6 +148,7 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
         binding.avatarImage.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") }
 
         getCurrentUser()
+        handleIntent(intent)
 
         setupLicenceSetting()
 
@@ -162,6 +168,11 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
         setupClientCertView()
     }
 
+    private fun handleIntent(intent: Intent) {
+        val extras: Bundle? = intent.extras
+        openedByNotificationWarning = extras?.getBoolean(KEY_SCROLL_TO_NOTIFICATION_CATEGORY) ?: false
+    }
+
     override fun onResume() {
         super.onResume()
         supportActionBar?.show()
@@ -210,6 +221,22 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
 
         themeTitles()
         themeSwitchPreferences()
+
+        if (openedByNotificationWarning) {
+            scrollToNotificationCategory()
+        }
+    }
+
+    @Suppress("MagicNumber")
+    private fun scrollToNotificationCategory() {
+        binding.scrollView.post {
+            val scrollViewLocation = IntArray(2)
+            val targetLocation = IntArray(2)
+            binding.scrollView.getLocationOnScreen(scrollViewLocation)
+            binding.settingsNotificationsCategory.getLocationOnScreen(targetLocation)
+            val offset = targetLocation[1] - scrollViewLocation[1]
+            binding.scrollView.scrollBy(0, offset)
+        }
     }
 
     private fun loadCapabilitiesAndUpdateSettings() {
@@ -255,11 +282,9 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
     }
 
     private fun setupNotificationSettings() {
-        binding.settingsNotificationsTitle.text = resources!!.getString(
-            R.string.nc_settings_notification_sounds_post_oreo
-        )
         setupNotificationSoundsSettings()
         setupNotificationPermissionSettings()
+        setupServerNotificationAppCheck()
     }
 
     @SuppressLint("StringFormatInvalid")
@@ -281,6 +306,10 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
                     resources!!.getString(R.string.nc_diagnose_battery_optimization_not_ignored)
                 binding.batteryOptimizationIgnored.setTextColor(resources.getColor(R.color.nc_darkRed, null))
 
+                if (openedByNotificationWarning) {
+                    DrawableUtils.blinkDrawable(binding.settingsBatteryOptimizationWrapper.background)
+                }
+
                 binding.settingsBatteryOptimizationWrapper.setOnClickListener {
                     val dialogText = String.format(
                         context.resources.getString(R.string.nc_ignore_battery_optimization_dialog_text),
@@ -313,12 +342,26 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
                     binding.ncDiagnoseNotificationPermissionSubtitle.setTextColor(
                         resources.getColor(R.color.high_emphasis_text, null)
                     )
+                    binding.settingsCallSound.isEnabled = true
+                    binding.settingsCallSound.alpha = ENABLED_ALPHA
+                    binding.settingsMessageSound.isEnabled = true
+                    binding.settingsMessageSound.alpha = ENABLED_ALPHA
                 } else {
                     binding.ncDiagnoseNotificationPermissionSubtitle.text =
                         resources.getString(R.string.nc_settings_notifications_declined)
                     binding.ncDiagnoseNotificationPermissionSubtitle.setTextColor(
                         resources.getColor(R.color.nc_darkRed, null)
                     )
+
+                    if (openedByNotificationWarning) {
+                        DrawableUtils.blinkDrawable(binding.settingsNotificationsPermissionWrapper.background)
+                    }
+
+                    binding.settingsCallSound.isEnabled = false
+                    binding.settingsCallSound.alpha = DISABLED_ALPHA
+                    binding.settingsMessageSound.isEnabled = false
+                    binding.settingsMessageSound.alpha = DISABLED_ALPHA
+
                     binding.settingsNotificationsPermissionWrapper.setOnClickListener {
                         requestPermissions(
                             arrayOf(Manifest.permission.POST_NOTIFICATIONS),
@@ -346,6 +389,10 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
                 ResourcesCompat.getColor(context.resources, R.color.nc_darkRed, null)
             )
             binding.callsRingtone.text = resources!!.getString(R.string.nc_common_disabled)
+
+            if (openedByNotificationWarning) {
+                DrawableUtils.blinkDrawable(binding.settingsCallSound.background)
+            }
         }
 
         if (NotificationUtils.isMessagesNotificationChannelEnabled(this)) {
@@ -357,6 +404,10 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
                 ResourcesCompat.getColor(context.resources, R.color.nc_darkRed, null)
             )
             binding.messagesRingtone.text = resources!!.getString(R.string.nc_common_disabled)
+
+            if (openedByNotificationWarning) {
+                DrawableUtils.blinkDrawable(binding.settingsMessageSound.background)
+            }
         }
 
         binding.settingsCallSound.setOnClickListener {
@@ -426,6 +477,24 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
         }
     }
 
+    private fun setupServerNotificationAppCheck() {
+        val serverNotificationAppInstalled =
+            userManager.currentUser.blockingGet().capabilities?.notificationsCapability?.features?.isNotEmpty() ?: false
+        if (!serverNotificationAppInstalled) {
+            binding.settingsServerNotificationAppWrapper.visibility = View.VISIBLE
+
+            val description = context.getString(R.string.nc_settings_contact_admin_of) + LINEBREAK +
+                userManager.currentUser.blockingGet().baseUrl!!
+
+            binding.settingsServerNotificationAppDescription.text = description
+            if (openedByNotificationWarning) {
+                DrawableUtils.blinkDrawable(binding.settingsServerNotificationAppWrapper.background)
+            }
+        } else {
+            binding.settingsServerNotificationAppWrapper.visibility = View.GONE
+        }
+    }
+
     private fun setupSourceCodeUrl() {
         if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_source_code_url))) {
             binding.settingsSourceCode.setOnClickListener {
@@ -646,8 +715,8 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
         startActivity(intent)
     }
 
-    private fun getRingtoneName(context: Context, ringtoneUri: Uri?): String {
-        return if (ringtoneUri == null) {
+    private fun getRingtoneName(context: Context, ringtoneUri: Uri?): String =
+        if (ringtoneUri == null) {
             resources!!.getString(R.string.nc_settings_no_ringtone)
         } else if ((NotificationUtils.DEFAULT_CALL_RINGTONE_URI == ringtoneUri.toString()) ||
             (NotificationUtils.DEFAULT_MESSAGE_RINGTONE_URI == ringtoneUri.toString())
@@ -657,11 +726,11 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
             val r = RingtoneManager.getRingtone(context, ringtoneUri)
             r.getTitle(context)
         }
-    }
 
     private fun themeSwitchPreferences() {
         binding.run {
             listOf(
+                settingsShowNotificationWarningSwitch,
                 settingsScreenLockSwitch,
                 settingsScreenSecuritySwitch,
                 settingsIncognitoKeyboardSwitch,
@@ -857,6 +926,19 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
     }
 
     private fun setupCheckables() {
+        binding.settingsShowNotificationWarningSwitch.isChecked =
+            appPreferences.showNotificationWarning
+
+        if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) {
+            binding.settingsShowNotificationWarning.setOnClickListener {
+                val isChecked = binding.settingsShowNotificationWarningSwitch.isChecked
+                binding.settingsShowNotificationWarningSwitch.isChecked = !isChecked
+                appPreferences.setShowNotificationWarning(!isChecked)
+            }
+        } else {
+            binding.settingsShowNotificationWarning.visibility = View.GONE
+        }
+
         binding.settingsScreenSecuritySwitch.isChecked = appPreferences.isScreenSecured
 
         binding.settingsIncognitoKeyboardSwitch.isChecked = appPreferences.isKeyboardIncognito
@@ -1067,16 +1149,14 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
 
             ConversationsListActivity.REQUEST_POST_NOTIFICATIONS_PERMISSION -> {
                 if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_DENIED) {
-                    Snackbar.make(
-                        binding.root,
-                        context.resources.getString(R.string.nc_settings_notifications_declined_hint),
-                        Snackbar.LENGTH_LONG
-                    ).show()
-                    Log.d(
-                        TAG,
-                        "Notification permission is denied. Either because user denied it when being asked. " +
-                            "Or permission is already denied and android decided to not offer the dialog."
-                    )
+                    try {
+                        val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
+                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                        intent.putExtra(Settings.EXTRA_APP_PACKAGE, BuildConfig.APPLICATION_ID)
+                        startActivity(intent)
+                    } catch (e: Exception) {
+                        Log.e(TAG, "Failed to open notification settings as fallback", e)
+                    }
                 }
             }
         }
@@ -1344,6 +1424,7 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
         private const val START_DELAY: Long = 5000
         private const val DISABLED_ALPHA: Float = 0.38f
         private const val ENABLED_ALPHA: Float = 1.0f
+        private const val LINEBREAK = "\n"
         const val HTTP_CODE_OK: Int = 200
         const val HTTP_ERROR_CODE_BAD_REQUEST: Int = 400
     }

+ 27 - 0
app/src/main/java/com/nextcloud/talk/utils/DrawableUtils.kt

@@ -6,14 +6,22 @@
  */
 package com.nextcloud.talk.utils
 
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.RippleDrawable
+import android.util.Log
 import com.nextcloud.talk.R
 import com.nextcloud.talk.utils.Mimetype.AUDIO_PREFIX
 import com.nextcloud.talk.utils.Mimetype.FOLDER
 import com.nextcloud.talk.utils.Mimetype.IMAGE_PREFIX
 import com.nextcloud.talk.utils.Mimetype.TEXT_PREFIX
 import com.nextcloud.talk.utils.Mimetype.VIDEO_PREFIX
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
 
 object DrawableUtils {
+    private val TAG = DrawableUtils::class.java.simpleName
 
     @Suppress("Detekt.LongMethod")
     fun getDrawableResourceIdForMimeType(mimetype: String?): Int {
@@ -153,4 +161,23 @@ object DrawableUtils {
             drawableMap["unknown"]!!
         }
     }
+
+    @Suppress("MagicNumber", "TooGenericExceptionCaught")
+    fun blinkDrawable(rippleView: Drawable) {
+        try {
+            (rippleView as RippleDrawable).let { rippleDrawable ->
+                CoroutineScope(Dispatchers.Main).launch {
+                    delay(1000L) // Wait 2 seconds before starting
+                    repeat(3) {
+                        rippleDrawable.state = intArrayOf(android.R.attr.state_pressed, android.R.attr.state_enabled)
+                        delay(250L) // Ripple active duration
+                        rippleDrawable.state = intArrayOf() // Reset state
+                        delay(250L) // Time between blinks
+                    }
+                }
+            }
+        } catch (e: Exception) {
+            Log.e(TAG, "Failed to blink Drawable", e)
+        }
+    }
 }

+ 1 - 0
app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt

@@ -80,4 +80,5 @@ object BundleKeys {
     const val KEY_CREDENTIALS: String = "KEY_CREDENTIALS"
     const val KEY_FIELD_MAP: String = "KEY_FIELD_MAP"
     const val KEY_CHAT_URL: String = "KEY_CHAT_URL"
+    const val KEY_SCROLL_TO_NOTIFICATION_CATEGORY: String = "KEY_SCROLL_TO_NOTIFICATION_CATEGORY"
 }

+ 3 - 0
app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferences.java

@@ -178,6 +178,9 @@ public interface AppPreferences {
 
     void deleteAllMessageQueuesFor(String userId);
 
+    boolean getShowNotificationWarning();
+
+    void setShowNotificationWarning(boolean showNotificationWarning);
 
     void clear();
 }

+ 14 - 0
app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt

@@ -544,6 +544,19 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
         }
     }
 
+    override fun getShowNotificationWarning(): Boolean {
+        return runBlocking { async {
+            readBoolean(SHOW_NOTIFICATION_WARNING, true).first()
+        } }.getCompleted()
+    }
+
+    override fun setShowNotificationWarning(showNotificationWarning: Boolean) =
+        runBlocking<Unit> {
+            async {
+                writeBoolean(SHOW_NOTIFICATION_WARNING, showNotificationWarning)
+            }
+        }
+
     override fun clear() {}
 
     private suspend fun writeString(key: String, value: String) =
@@ -628,6 +641,7 @@ class AppPreferencesImpl(val context: Context) : AppPreferences {
         const val PHONE_BOOK_INTEGRATION_LAST_RUN = "phone_book_integration_last_run"
         const val TYPING_STATUS = "typing_status"
         const val MESSAGE_QUEUE = "@message_queue"
+        const val SHOW_NOTIFICATION_WARNING = "show_notification_warning"
         private fun String.convertStringToArray(): Array<Float> {
             var varString = this
             val floatList = mutableListOf<Float>()

+ 12 - 0
app/src/main/res/layout/activity_conversations.xml

@@ -37,6 +37,18 @@
             android:visibility="gone"
             tools:visibility="visible" />
 
+        <com.google.android.material.textview.MaterialTextView
+            android:id="@+id/chat_list_notification_warning"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@color/nc_warning"
+            android:gravity="center"
+            android:minHeight="40dp"
+            android:text="@string/nc_notification_warning"
+            android:textColor="@color/white"
+            android:visibility="gone"
+            tools:visibility="visible" />
+
         <com.google.android.material.card.MaterialCardView
             android:id="@+id/search_toolbar"
             android:layout_width="match_parent"

+ 73 - 20
app/src/main/res/layout/activity_settings.xml

@@ -185,7 +185,6 @@
                         android:text=""
                         tools:ignore="SpeakableTextPresentCheck" />
 
-
                 </com.google.android.material.textfield.TextInputLayout>
 
             </LinearLayout>
@@ -201,7 +200,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_margin="@dimen/standard_margin"
-                    android:text="@string/nc_settings_notification_sounds"
+                    android:text="@string/nc_settings_notification_sounds_post_oreo"
                     android:textSize="@dimen/headline_text_size"
                     android:textStyle="bold"/>
 
@@ -211,19 +210,77 @@
                     android:layout_height="wrap_content"
                     android:orientation="vertical">
 
+                    <LinearLayout
+                        android:id="@+id/settings_show_notification_warning"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:background="?android:attr/selectableItemBackground"
+                        android:orientation="horizontal"
+                        android:padding="@dimen/standard_padding">
+
+                        <LinearLayout
+                            android:layout_width="0dp"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"
+                            android:orientation="vertical">
+
+                            <com.google.android.material.textview.MaterialTextView
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:text="@string/nc_show_notification_warning_title"
+                                android:textSize="@dimen/headline_text_size"/>
+
+                            <com.google.android.material.textview.MaterialTextView
+                                android:id="@+id/settings_show_notification_warning_summary"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:text="@string/nc_show_notification_warning_description"/>
+                        </LinearLayout>
+
+                        <com.google.android.material.materialswitch.MaterialSwitch
+                            android:id="@+id/settings_show_notification_warning_switch"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:clickable="false"
+                            android:gravity="center_vertical"/>
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:id="@+id/settings_server_notification_app_wrapper"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:background="?android:attr/selectableItemBackground"
+                        android:orientation="vertical"
+                        android:padding="@dimen/standard_padding">
+
+                        <com.google.android.material.textview.MaterialTextView
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="@string/nc_settings_server_notification_app_not_installed_title"
+                            android:textSize="@dimen/headline_text_size"/>
+
+                        <com.google.android.material.textview.MaterialTextView
+                            android:id="@+id/settings_server_notification_app_description"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:textColor="@color/nc_darkRed"
+                            android:textSize="@dimen/supporting_text_text_size"
+                            tools:text="Please contact the admin of www.example.com"/>
+                    </LinearLayout>
+
                     <LinearLayout
                         android:id="@+id/settings_notifications_permission_wrapper"
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
+                        android:background="?android:attr/selectableItemBackground"
                         android:orientation="vertical"
-                        android:padding="@dimen/standard_padding"
-                        android:background="?android:attr/selectableItemBackground">
+                        android:padding="@dimen/standard_padding">
 
                         <com.google.android.material.textview.MaterialTextView
                             android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
-                            android:textSize="@dimen/headline_text_size"
-                            android:text="@string/nc_diagnose_notification_permission" />
+                            android:text="@string/nc_diagnose_notification_permission"
+                            android:textSize="@dimen/headline_text_size" />
 
                         <com.google.android.material.textview.MaterialTextView
                             android:id="@+id/nc_diagnose_notification_permission_subtitle"
@@ -235,15 +292,15 @@
                         android:id="@+id/settings_battery_optimization_wrapper"
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
+                        android:background="?android:attr/selectableItemBackground"
                         android:orientation="vertical"
-                        android:padding="@dimen/standard_padding"
-                        android:background="?android:attr/selectableItemBackground">
+                        android:padding="@dimen/standard_padding">
 
                         <com.google.android.material.textview.MaterialTextView
                             android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
-                            android:textSize="@dimen/headline_text_size"
-                            android:text="@string/nc_diagnose_battery_optimization_title" />
+                            android:text="@string/nc_diagnose_battery_optimization_title"
+                            android:textSize="@dimen/headline_text_size" />
 
                         <com.google.android.material.textview.MaterialTextView
                             android:id="@+id/battery_optimization_ignored"
@@ -251,6 +308,7 @@
                             android:layout_height="wrap_content"
                             tools:text="@string/nc_diagnose_battery_optimization_ignored"/>
                     </LinearLayout>
+
                 </LinearLayout>
 
                 <LinearLayout
@@ -273,12 +331,13 @@
                     android:id="@+id/settings_call_sound"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:orientation="vertical">
+                    android:background="?android:attr/selectableItemBackground"
+                    android:orientation="vertical"
+                    android:padding="@dimen/standard_padding">
 
                     <com.google.android.material.textview.MaterialTextView
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:layout_margin="@dimen/standard_margin"
                         android:text="@string/nc_settings_call_ringtone"
                         android:textSize="@dimen/headline_text_size"/>
 
@@ -286,8 +345,6 @@
                         android:id="@+id/calls_ringtone"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:layout_marginStart="@dimen/standard_margin"
-                        android:layout_marginBottom="@dimen/standard_margin"
                         android:text="@string/nc_settings_default_ringtone"
                         android:textSize="@dimen/supporting_text_text_size"/>
                 </LinearLayout>
@@ -296,13 +353,13 @@
                     android:id="@+id/settings_message_sound"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:background="?android:attr/selectableItemBackground"
                     android:orientation="vertical"
-                    android:background="?android:attr/selectableItemBackground">
+                    android:padding="@dimen/standard_padding">
 
                     <com.google.android.material.textview.MaterialTextView
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:layout_margin="@dimen/standard_margin"
                         android:text="@string/nc_settings_other_notifications_ringtone"
                         android:textSize="@dimen/headline_text_size"/>
 
@@ -310,13 +367,9 @@
                         android:id="@+id/messages_ringtone"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:layout_marginStart="@dimen/standard_margin"
-                        android:layout_marginBottom="@dimen/standard_margin"
                         android:text="@string/nc_settings_default_ringtone"
                         android:textSize="@dimen/supporting_text_text_size"/>
-
                 </LinearLayout>
-
             </LinearLayout>
 
             <LinearLayout

+ 1 - 0
app/src/main/res/values/colors.xml

@@ -92,5 +92,6 @@
     <color name="icon_on_bg_default">#99000000</color>
     <color name="badge_color">#EF3B02</color>
     <color name="secondary_button_background">#DBE2E9</color>
+    <color name="nc_warning">#FF9800</color>
 
 </resources>

+ 6 - 2
app/src/main/res/values/strings.xml

@@ -101,7 +101,6 @@ How to translate with transifex:
     <string name="nc_settings_no_talk_installed">Talk app is not installed on the server you tried to authenticate against</string>
     <string name="nc_settings_account_updated">Your already existing account was updated, instead of adding a new one</string>
     <string name="nc_account_scheduled_for_deletion">The account is scheduled for deletion, and cannot be changed</string>
-    <string name="nc_settings_notification_sounds">Notification sounds</string>
     <string name="nc_settings_notification_sounds_post_oreo">Notifications</string>
     <string name="nc_settings_call_ringtone">Calls</string>
     <string name="nc_settings_call_ringtone_key" translatable="false">call_ringtone</string>
@@ -109,6 +108,8 @@ How to translate with transifex:
     <string name="nc_settings_message_ringtone_key" translatable="false">message_ringtone</string>
     <string name="nc_settings_default_ringtone" translatable="false">Librem by feandesign</string>
     <string name="nc_settings_no_ringtone">No sound</string>
+    <string name="nc_settings_server_notification_app_not_installed_title">Server notifications app not installed</string>
+    <string name="nc_settings_contact_admin_of">Please contact the administrator of</string>
 
     <string name="nc_settings_appearance">Appearance</string>
     <string name="nc_settings_theme_title">Theme</string>
@@ -181,6 +182,9 @@ How to translate with transifex:
 
     <string name="nc_ignore_battery_optimization_dialog_title">Ignore battery optimization</string>
     <string name="nc_ignore_battery_optimization_dialog_text">Battery optimization is not ignored. This should be changed to make sure that notifications work in the background! Please click OK and select \"All apps\" -> %1$s -> Do not optimize</string>
+    <string name="nc_show_notification_warning_title">Show notification warning</string>
+    <string name="nc_show_notification_warning_description">When notifications are not set up correctly, show a warning</string>
+    <string name="nc_notification_warning">Notifications are not set up correctly</string>
 
     <string name="nc_diagnose_meta_category_title">Meta information</string>
     <string name="nc_diagnose_meta_system_report_date">Generation of system report</string>
@@ -195,7 +199,7 @@ How to translate with transifex:
     <string name="nc_diagnose_gplay_available_yes">Google Play services are available</string>
     <string name="nc_diagnose_gplay_available_no">Google Play services are not available. Notifications are not supported</string>
     <string name="nc_diagnose_battery_optimization_title">Battery settings</string>
-    <string name="nc_diagnose_battery_optimization_not_ignored">Battery optimization is not ignored. This should be changed!</string>
+    <string name="nc_diagnose_battery_optimization_not_ignored">Battery optimization is enabled which might cause issues. You should disable battery optimization!</string>
     <string name="nc_diagnose_battery_optimization_ignored">Battery optimization is ignored, all fine</string>
     <string name="nc_diagnose_notification_permission">Notification permissions</string>
     <string name="nc_diagnose_notification_calls_channel_permission">Calls notification channel enabled?</string>