Check if migration disabled notif

This commit is contained in:
Valere 2020-07-27 16:55:11 +02:00
parent f7b9fc3bb1
commit 6f5b3371fe
10 changed files with 209 additions and 6 deletions

View File

@ -21,6 +21,7 @@ Bugfix 🐛:
- Fix timeline items not loading when there are only filtered events - Fix timeline items not loading when there are only filtered events
- Fix "Voice & Video" grayed out in Settings (#1733) - Fix "Voice & Video" grayed out in Settings (#1733)
- Fix Allow VOIP call in all rooms with 2 participants (even if not DM) - Fix Allow VOIP call in all rooms with 2 participants (even if not DM)
- Migration from old client does not enable notifications (#1723)
Translations 🗣: Translations 🗣:
- -

View File

@ -16,11 +16,18 @@
package im.vector.riotx.core.preference package im.vector.riotx.core.preference
import android.animation.Animator
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
import android.content.Context import android.content.Context
import android.graphics.Color
import android.util.AttributeSet import android.util.AttributeSet
import android.widget.TextView import android.widget.TextView
import androidx.core.animation.doOnEnd
import androidx.preference.PreferenceViewHolder import androidx.preference.PreferenceViewHolder
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import im.vector.riotx.R
import im.vector.riotx.features.themes.ThemeUtils
/** /**
* Switch preference with title on multiline (only used in XML) * Switch preference with title on multiline (only used in XML)
@ -41,10 +48,49 @@ class VectorSwitchPreference : SwitchPreference {
isIconSpaceReserved = true isIconSpaceReserved = true
} }
var isHighlighted = false
set(value) {
field = value
notifyChanged()
}
var currentHighlightAnimator: Animator? = null
override fun onBindViewHolder(holder: PreferenceViewHolder) { override fun onBindViewHolder(holder: PreferenceViewHolder) {
// display the title in multi-line to avoid ellipsis. // display the title in multi-line to avoid ellipsis.
holder.itemView.findViewById<TextView>(android.R.id.title)?.isSingleLine = false holder.itemView.findViewById<TextView>(android.R.id.title)?.isSingleLine = false
// cancel existing animation (find a way to resume if happens during anim?)
currentHighlightAnimator?.cancel()
val itemView = holder.itemView
if (isHighlighted) {
val colorFrom = Color.TRANSPARENT
val colorTo = ThemeUtils.getColor(itemView.context, R.attr.colorControlHighlight)
currentHighlightAnimator = ValueAnimator.ofObject(ArgbEvaluator(), colorFrom, colorTo).apply {
duration = 250 // milliseconds
addUpdateListener { animator ->
itemView.setBackgroundColor(animator.animatedValue as Int)
}
doOnEnd {
currentHighlightAnimator = ValueAnimator.ofObject(ArgbEvaluator(), colorTo, colorFrom).apply {
duration = 250 // milliseconds
addUpdateListener { animator ->
itemView.setBackgroundColor(animator.animatedValue as Int)
}
doOnEnd {
isHighlighted = false
}
start()
}
}
startDelay = 200
start()
}
} else {
itemView.setBackgroundColor(Color.TRANSPARENT)
}
super.onBindViewHolder(holder) super.onBindViewHolder(holder)
} }
} }

View File

@ -42,10 +42,13 @@ import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.pushers.PushersManager import im.vector.riotx.core.pushers.PushersManager
import im.vector.riotx.features.disclaimer.showDisclaimerDialog import im.vector.riotx.features.disclaimer.showDisclaimerDialog
import im.vector.riotx.features.notifications.NotificationDrawerManager import im.vector.riotx.features.notifications.NotificationDrawerManager
import im.vector.riotx.features.popup.DefaultVectorAlert
import im.vector.riotx.features.popup.PopupAlertManager import im.vector.riotx.features.popup.PopupAlertManager
import im.vector.riotx.features.popup.VerificationVectorAlert import im.vector.riotx.features.popup.VerificationVectorAlert
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.riotx.features.settings.VectorPreferences import im.vector.riotx.features.settings.VectorPreferences
import im.vector.riotx.features.settings.VectorSettingsActivity
import im.vector.riotx.features.themes.ThemeUtils
import im.vector.riotx.features.workers.signout.ServerBackupStatusViewModel import im.vector.riotx.features.workers.signout.ServerBackupStatusViewModel
import im.vector.riotx.features.workers.signout.ServerBackupStatusViewState import im.vector.riotx.features.workers.signout.ServerBackupStatusViewState
import im.vector.riotx.push.fcm.FcmHelper import im.vector.riotx.push.fcm.FcmHelper
@ -69,7 +72,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
@Inject lateinit var viewModelFactory: HomeActivityViewModel.Factory @Inject lateinit var viewModelFactory: HomeActivityViewModel.Factory
private val serverBackupStatusViewModel: ServerBackupStatusViewModel by viewModel() private val serverBackupStatusViewModel: ServerBackupStatusViewModel by viewModel()
@Inject lateinit var serverBackupviewModelFactory: ServerBackupStatusViewModel.Factory @Inject lateinit var serverBackupviewModelFactory: ServerBackupStatusViewModel.Factory
@Inject lateinit var activeSessionHolder: ActiveSessionHolder @Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler @Inject lateinit var vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
@ -134,6 +137,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
when (it) { when (it) {
is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it) is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it)
is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it) is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
}.exhaustive }.exhaustive
} }
homeActivityViewModel.subscribe(this) { renderState(it) } homeActivityViewModel.subscribe(this) { renderState(it) }
@ -193,6 +197,42 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
} }
} }
private fun handlePromptToEnablePush() {
popupAlertManager.postVectorAlert(
DefaultVectorAlert(
uid = "enablePush",
title = getString(R.string.alert_push_are_disabled_title),
description = getString(R.string.alert_push_are_disabled_description),
iconId = R.drawable.ic_room_actions_notifications_mutes,
shouldBeDisplayedIn = {
it is HomeActivity
}
).apply {
colorInt = ThemeUtils.getColor(this@HomeActivity, R.attr.vctr_notice_secondary)
contentAction = Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
// action(it)
homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed)
it.navigator.openSettings(it, VectorSettingsActivity.EXTRA_DIRECT_ACCESS_NOTIFICATIONS)
}
}
dismissedAction = Runnable {
homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed)
}
addButton(getString(R.string.dismiss), Runnable {
homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed)
}, true)
addButton(getString(R.string.settings), Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let {
// action(it)
homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed)
it.navigator.openSettings(it, VectorSettingsActivity.EXTRA_DIRECT_ACCESS_NOTIFICATIONS)
}
}, true)
}
)
}
private fun promptSecurityEvent(userItem: MatrixItem.UserItem?, titleRes: Int, descRes: Int, action: ((VectorBaseActivity) -> Unit)) { private fun promptSecurityEvent(userItem: MatrixItem.UserItem?, titleRes: Int, descRes: Int, action: ((VectorBaseActivity) -> Unit)) {
popupAlertManager.postVectorAlert( popupAlertManager.postVectorAlert(
VerificationVectorAlert( VerificationVectorAlert(

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotx.features.home
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class HomeActivityViewActions : VectorViewModelAction {
object PushPromptHasBeenReviewed : HomeActivityViewActions()
}

View File

@ -22,4 +22,5 @@ import im.vector.riotx.core.platform.VectorViewEvents
sealed class HomeActivityViewEvents : VectorViewEvents { sealed class HomeActivityViewEvents : VectorViewEvents {
data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents() data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem?) : HomeActivityViewEvents()
data class OnNewSession(val userItem: MatrixItem.UserItem?, val waitForIncomingRequest: Boolean = true) : HomeActivityViewEvents() data class OnNewSession(val userItem: MatrixItem.UserItem?, val waitForIncomingRequest: Boolean = true) : HomeActivityViewEvents()
object PromptToEnableSessionPush : HomeActivityViewEvents()
} }

View File

@ -16,6 +16,7 @@
package im.vector.riotx.features.home package im.vector.riotx.features.home
import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
@ -23,24 +24,32 @@ import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.NoOpMatrixCallback import im.vector.matrix.android.api.NoOpMatrixCallback
import im.vector.matrix.android.api.pushrules.RuleIds
import im.vector.matrix.android.api.session.InitialSyncProgressService import im.vector.matrix.android.api.session.InitialSyncProgressService
import im.vector.matrix.android.api.session.room.model.Membership
import im.vector.matrix.android.api.session.room.roomSummaryQueryParams
import im.vector.matrix.android.api.util.toMatrixItem import im.vector.matrix.android.api.util.toMatrixItem
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.matrix.rx.asObservable import im.vector.matrix.rx.asObservable
import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.platform.EmptyAction import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.login.ReAuthHelper import im.vector.riotx.features.login.ReAuthHelper
import im.vector.riotx.features.settings.VectorPreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
class HomeActivityViewModel @AssistedInject constructor( class HomeActivityViewModel @AssistedInject constructor(
@Assisted initialState: HomeActivityViewState, @Assisted initialState: HomeActivityViewState,
@Assisted private val args: HomeActivityArgs, @Assisted private val args: HomeActivityArgs,
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
private val reAuthHelper: ReAuthHelper private val reAuthHelper: ReAuthHelper,
) : VectorViewModel<HomeActivityViewState, EmptyAction, HomeActivityViewEvents>(initialState) { private val vectorPreferences: VectorPreferences
) : VectorViewModel<HomeActivityViewState, HomeActivityViewActions, HomeActivityViewEvents>(initialState) {
@AssistedInject.Factory @AssistedInject.Factory
interface Factory { interface Factory {
@ -62,6 +71,7 @@ class HomeActivityViewModel @AssistedInject constructor(
init { init {
observeInitialSync() observeInitialSync()
mayBeInitializeCrossSigning() mayBeInitializeCrossSigning()
checkSessionPushIsOn()
} }
private fun observeInitialSync() { private fun observeInitialSync() {
@ -115,6 +125,41 @@ class HomeActivityViewModel @AssistedInject constructor(
} }
} }
/*
* After migration from riot to element some users reported that their
* push setting for the session was set to off
* In order to mitigate this, we want to display a popup once to the user
* giving him the option to review this setting
*/
private fun checkSessionPushIsOn() {
viewModelScope.launch(Dispatchers.IO) {
// Don't do that if it's a login or a register (pass in memory)
if (reAuthHelper.data != null) return@launch
// Check if disabled for this device
if (!vectorPreferences.areNotificationEnabledForDevice()) {
// Check if set at account level
val mRuleMaster = activeSessionHolder.getSafeActiveSession()
?.getPushRules()
?.getAllRules()
?.find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
if (mRuleMaster?.enabled == false) {
// So push are enabled at account level but not for this session
// Let's check that there are some rooms?
val knownRooms = activeSessionHolder.getSafeActiveSession()?.getRoomSummaries(roomSummaryQueryParams {
memberships = Membership.activeMemberships()
})?.size ?: 0
// Prompt once to the user
if (knownRooms > 1 && !vectorPreferences.didAskUserToEnableSessionPush()) {
// delay a bit
delay(1500)
_viewEvents.post(HomeActivityViewEvents.PromptToEnableSessionPush)
}
}
}
}
}
private fun maybeBootstrapCrossSigning() { private fun maybeBootstrapCrossSigning() {
// In case of account creation, it is already done before // In case of account creation, it is already done before
if (args.accountCreation) return if (args.accountCreation) return
@ -167,7 +212,11 @@ class HomeActivityViewModel @AssistedInject constructor(
}) })
} }
override fun handle(action: EmptyAction) { override fun handle(action: HomeActivityViewActions) {
// NA when (action) {
HomeActivityViewActions.PushPromptHasBeenReviewed -> {
vectorPreferences.setDidAskUserToEnableSessionPush()
}
}.exhaustive
} }
} }

View File

@ -167,6 +167,8 @@ class VectorPreferences @Inject constructor(private val context: Context) {
private const val DID_ASK_TO_USE_ANALYTICS_TRACKING_KEY = "DID_ASK_TO_USE_ANALYTICS_TRACKING_KEY" private const val DID_ASK_TO_USE_ANALYTICS_TRACKING_KEY = "DID_ASK_TO_USE_ANALYTICS_TRACKING_KEY"
private const val SETTINGS_DISPLAY_ALL_EVENTS_KEY = "SETTINGS_DISPLAY_ALL_EVENTS_KEY" private const val SETTINGS_DISPLAY_ALL_EVENTS_KEY = "SETTINGS_DISPLAY_ALL_EVENTS_KEY"
private const val DID_ASK_TO_ENABLE_SESSION_PUSH = "DID_ASK_TO_ENABLE_SESSION_PUSH"
private const val MEDIA_SAVING_3_DAYS = 0 private const val MEDIA_SAVING_3_DAYS = 0
private const val MEDIA_SAVING_1_WEEK = 1 private const val MEDIA_SAVING_1_WEEK = 1
private const val MEDIA_SAVING_1_MONTH = 2 private const val MEDIA_SAVING_1_MONTH = 2
@ -285,6 +287,16 @@ class VectorPreferences @Inject constructor(private val context: Context) {
return BuildConfig.DEBUG || (developerMode() && defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY, false)) return BuildConfig.DEBUG || (developerMode() && defaultPrefs.getBoolean(SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY, false))
} }
fun didAskUserToEnableSessionPush(): Boolean {
return defaultPrefs.getBoolean(DID_ASK_TO_ENABLE_SESSION_PUSH, false)
}
fun setDidAskUserToEnableSessionPush() {
defaultPrefs.edit {
putBoolean(DID_ASK_TO_ENABLE_SESSION_PUSH, true)
}
}
/** /**
* Tells if we have already asked the user to disable battery optimisations on android >= M devices. * Tells if we have already asked the user to disable battery optimisations on android >= M devices.
* *

View File

@ -70,6 +70,11 @@ class VectorSettingsActivity : VectorBaseActivity(),
VectorSettingsDevicesFragment::class.java, VectorSettingsDevicesFragment::class.java,
null, null,
FRAGMENT_TAG) FRAGMENT_TAG)
EXTRA_DIRECT_ACCESS_NOTIFICATIONS -> {
requestHighlightPreferenceKeyOnResume(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY)
replaceFragment(R.id.vector_settings_page, VectorSettingsNotificationPreferenceFragment::class.java, null, FRAGMENT_TAG)
}
else -> else ->
replaceFragment(R.id.vector_settings_page, VectorSettingsRootFragment::class.java, null, FRAGMENT_TAG) replaceFragment(R.id.vector_settings_page, VectorSettingsRootFragment::class.java, null, FRAGMENT_TAG)
} }
@ -140,6 +145,7 @@ class VectorSettingsActivity : VectorBaseActivity(),
const val EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY = 2 const val EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY = 2
const val EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS = 3 const val EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY_MANAGE_SESSIONS = 3
const val EXTRA_DIRECT_ACCESS_GENERAL = 4 const val EXTRA_DIRECT_ACCESS_GENERAL = 4
const val EXTRA_DIRECT_ACCESS_NOTIFICATIONS = 5
private const val FRAGMENT_TAG = "VectorSettingsPreferencesFragment" private const val FRAGMENT_TAG = "VectorSettingsPreferencesFragment"
} }

View File

@ -17,6 +17,7 @@
package im.vector.riotx.features.settings package im.vector.riotx.features.settings
import android.app.Activity import android.app.Activity
import android.content.Context
import android.content.Intent import android.content.Intent
import android.media.RingtoneManager import android.media.RingtoneManager
import android.net.Uri import android.net.Uri
@ -46,6 +47,8 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
override var titleRes: Int = R.string.settings_notifications override var titleRes: Int = R.string.settings_notifications
override val preferenceXmlRes = R.xml.vector_settings_notifications override val preferenceXmlRes = R.xml.vector_settings_notifications
private var interactionListener: VectorSettingsFragmentInteractionListener? = null
override fun bindPref() { override fun bindPref() {
findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY)!!.let { pref -> findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY)!!.let { pref ->
val pushRuleService = session val pushRuleService = session
@ -139,6 +142,24 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
activeSessionHolder.getSafeActiveSession()?.refreshPushers() activeSessionHolder.getSafeActiveSession()?.refreshPushers()
interactionListener?.requestedKeyToHighlight()?.let { key ->
interactionListener?.requestHighlightPreferenceKeyOnResume(null)
val preference = findPreference<VectorSwitchPreference>(key)
preference?.isHighlighted = true
}
}
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is VectorSettingsFragmentInteractionListener) {
interactionListener = context
}
}
override fun onDetach() {
interactionListener = null
super.onDetach()
} }
override fun onPreferenceTreeClick(preference: Preference?): Boolean { override fun onPreferenceTreeClick(preference: Preference?): Boolean {

View File

@ -78,6 +78,7 @@
<string name="active_call">Active call</string> <string name="active_call">Active call</string>
<string name="play_video">Play</string> <string name="play_video">Play</string>
<string name="pause_video">Pause</string> <string name="pause_video">Pause</string>
<string name="dismiss">Dismiss</string>
<!-- First param will be replace by the value of ongoing_conference_call_voice, and second one by the value of ongoing_conference_call_video --> <!-- First param will be replace by the value of ongoing_conference_call_voice, and second one by the value of ongoing_conference_call_video -->
@ -2568,4 +2569,7 @@ Not all features in Riot are implemented in Element yet. Main missing (and comin
<string name="member_banned_by">Banned by %1$s</string> <string name="member_banned_by">Banned by %1$s</string>
<string name="failed_to_unban">Failed to UnBan user</string> <string name="failed_to_unban">Failed to UnBan user</string>
<string name="alert_push_are_disabled_title">Push notifications are disabled</string>
<string name="alert_push_are_disabled_description">Review your settings to enable push notifications</string>
</resources> </resources>