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 "Voice & Video" grayed out in Settings (#1733)
- Fix Allow VOIP call in all rooms with 2 participants (even if not DM)
- Migration from old client does not enable notifications (#1723)
Translations 🗣:
-

View file

@ -16,11 +16,18 @@
package im.vector.riotx.core.preference
import android.animation.Animator
import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.widget.TextView
import androidx.core.animation.doOnEnd
import androidx.preference.PreferenceViewHolder
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)
@ -41,10 +48,49 @@ class VectorSwitchPreference : SwitchPreference {
isIconSpaceReserved = true
}
var isHighlighted = false
set(value) {
field = value
notifyChanged()
}
var currentHighlightAnimator: Animator? = null
override fun onBindViewHolder(holder: PreferenceViewHolder) {
// display the title in multi-line to avoid ellipsis.
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)
}
}

View file

@ -42,10 +42,13 @@ import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.pushers.PushersManager
import im.vector.riotx.features.disclaimer.showDisclaimerDialog
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.VerificationVectorAlert
import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler
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.ServerBackupStatusViewState
import im.vector.riotx.push.fcm.FcmHelper
@ -69,7 +72,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
@Inject lateinit var viewModelFactory: HomeActivityViewModel.Factory
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 vectorUncaughtExceptionHandler: VectorUncaughtExceptionHandler
@ -134,6 +137,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
when (it) {
is HomeActivityViewEvents.AskPasswordToInitCrossSigning -> handleAskPasswordToInitCrossSigning(it)
is HomeActivityViewEvents.OnNewSession -> handleOnNewSession(it)
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
}.exhaustive
}
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)) {
popupAlertManager.postVectorAlert(
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 {
data class AskPasswordToInitCrossSigning(val userItem: MatrixItem.UserItem?) : 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
import androidx.lifecycle.viewModelScope
import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
@ -23,24 +24,32 @@ import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback
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.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.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.matrix.rx.asObservable
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.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
class HomeActivityViewModel @AssistedInject constructor(
@Assisted initialState: HomeActivityViewState,
@Assisted private val args: HomeActivityArgs,
private val activeSessionHolder: ActiveSessionHolder,
private val reAuthHelper: ReAuthHelper
) : VectorViewModel<HomeActivityViewState, EmptyAction, HomeActivityViewEvents>(initialState) {
private val reAuthHelper: ReAuthHelper,
private val vectorPreferences: VectorPreferences
) : VectorViewModel<HomeActivityViewState, HomeActivityViewActions, HomeActivityViewEvents>(initialState) {
@AssistedInject.Factory
interface Factory {
@ -62,6 +71,7 @@ class HomeActivityViewModel @AssistedInject constructor(
init {
observeInitialSync()
mayBeInitializeCrossSigning()
checkSessionPushIsOn()
}
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() {
// In case of account creation, it is already done before
if (args.accountCreation) return
@ -167,7 +212,11 @@ class HomeActivityViewModel @AssistedInject constructor(
})
}
override fun handle(action: EmptyAction) {
// NA
override fun handle(action: HomeActivityViewActions) {
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 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_1_WEEK = 1
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))
}
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.
*

View file

@ -70,6 +70,11 @@ class VectorSettingsActivity : VectorBaseActivity(),
VectorSettingsDevicesFragment::class.java,
null,
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 ->
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_MANAGE_SESSIONS = 3
const val EXTRA_DIRECT_ACCESS_GENERAL = 4
const val EXTRA_DIRECT_ACCESS_NOTIFICATIONS = 5
private const val FRAGMENT_TAG = "VectorSettingsPreferencesFragment"
}

View file

@ -17,6 +17,7 @@
package im.vector.riotx.features.settings
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.media.RingtoneManager
import android.net.Uri
@ -46,6 +47,8 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
override var titleRes: Int = R.string.settings_notifications
override val preferenceXmlRes = R.xml.vector_settings_notifications
private var interactionListener: VectorSettingsFragmentInteractionListener? = null
override fun bindPref() {
findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY)!!.let { pref ->
val pushRuleService = session
@ -139,6 +142,24 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
override fun onResume() {
super.onResume()
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 {

View file

@ -78,6 +78,7 @@
<string name="active_call">Active call</string>
<string name="play_video">Play</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 -->
@ -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="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>