mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 11:59:12 +03:00
Check if migration disabled notif
This commit is contained in:
parent
f7b9fc3bb1
commit
6f5b3371fe
10 changed files with 209 additions and 6 deletions
|
@ -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 🗣:
|
||||
-
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue