Merge pull request #5941 from vector-im/feature/bma/konfetti_speed

No animation -> no chat effect
This commit is contained in:
Benoit Marty 2022-05-09 17:05:53 +02:00 committed by GitHub
commit e35bb772c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 56 additions and 32 deletions

1
changelog.d/5941.bugfix Normal file
View File

@ -0,0 +1 @@
If animations are disable on the System, chat effects and confetti will be disabled too

View File

@ -31,7 +31,7 @@ class TestBatteryOptimization @Inject constructor(
) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) { ) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
if (isIgnoringBatteryOptimizations(context)) { if (context.isIgnoringBatteryOptimizations()) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_success) description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_success)
status = TestStatus.SUCCESS status = TestStatus.SUCCESS
quickFix = null quickFix = null

View File

@ -43,21 +43,20 @@ import im.vector.app.features.notifications.NotificationUtils
* This user option appears on Android M but Android O enforces its usage and kills apps not * This user option appears on Android M but Android O enforces its usage and kills apps not
* authorised by the user to run in background. * authorised by the user to run in background.
* *
* @param context the context
* @return true if battery optimisations are ignored * @return true if battery optimisations are ignored
*/ */
fun isIgnoringBatteryOptimizations(context: Context): Boolean { fun Context.isIgnoringBatteryOptimizations(): Boolean {
// no issue before Android M, battery optimisations did not exist // no issue before Android M, battery optimisations did not exist
return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
context.getSystemService<PowerManager>()?.isIgnoringBatteryOptimizations(context.packageName) == true getSystemService<PowerManager>()?.isIgnoringBatteryOptimizations(packageName) == true
} }
fun isAirplaneModeOn(context: Context): Boolean { fun Context.isAirplaneModeOn(): Boolean {
return Settings.Global.getInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) != 0 return Settings.Global.getInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) != 0
} }
fun isAnimationDisabled(context: Context): Boolean { fun Context.isAnimationEnabled(): Boolean {
return Settings.Global.getFloat(context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f) == 0f return Settings.Global.getFloat(contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f) != 0f
} }
/** /**

View File

@ -105,6 +105,7 @@ import im.vector.app.core.utils.colorizeMatchingText
import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.createJSonViewerStyleProvider import im.vector.app.core.utils.createJSonViewerStyleProvider
import im.vector.app.core.utils.createUIHandler import im.vector.app.core.utils.createUIHandler
import im.vector.app.core.utils.isAnimationEnabled
import im.vector.app.core.utils.isValidUrl import im.vector.app.core.utils.isValidUrl
import im.vector.app.core.utils.onPermissionDeniedDialog import im.vector.app.core.utils.onPermissionDeniedDialog
import im.vector.app.core.utils.onPermissionDeniedSnackbar import im.vector.app.core.utils.onPermissionDeniedSnackbar
@ -586,6 +587,10 @@ class TimelineFragment @Inject constructor(
} }
private fun handleChatEffect(chatEffect: ChatEffect) { private fun handleChatEffect(chatEffect: ChatEffect) {
if (!requireContext().isAnimationEnabled()) {
Timber.d("Do not perform chat effect, animations are disabled.")
return
}
when (chatEffect) { when (chatEffect) {
ChatEffect.CONFETTI -> { ChatEffect.CONFETTI -> {
views.viewKonfetti.isVisible = true views.viewKonfetti.isVisible = true
@ -666,11 +671,13 @@ class TimelineFragment @Inject constructor(
).apply { ).apply {
directListener = { granted -> directListener = { granted ->
if (granted) { if (granted) {
timelineViewModel.handle(RoomDetailAction.EnsureNativeWidgetAllowed( timelineViewModel.handle(
RoomDetailAction.EnsureNativeWidgetAllowed(
widget = it.widget, widget = it.widget,
userJustAccepted = true, userJustAccepted = true,
grantedEvents = it.grantedEvents grantedEvents = it.grantedEvents
)) )
)
} }
} }
} }
@ -791,25 +798,29 @@ class TimelineFragment @Inject constructor(
override fun onSendVoiceMessage() { override fun onSendVoiceMessage() {
messageComposerViewModel.handle( messageComposerViewModel.handle(
MessageComposerAction.EndRecordingVoiceMessage(isCancelled = false, rootThreadEventId = getRootThreadEventId())) MessageComposerAction.EndRecordingVoiceMessage(isCancelled = false, rootThreadEventId = getRootThreadEventId())
)
updateRecordingUiState(RecordingUiState.Idle) updateRecordingUiState(RecordingUiState.Idle)
} }
override fun onDeleteVoiceMessage() { override fun onDeleteVoiceMessage() {
messageComposerViewModel.handle( messageComposerViewModel.handle(
MessageComposerAction.EndRecordingVoiceMessage(isCancelled = true, rootThreadEventId = getRootThreadEventId())) MessageComposerAction.EndRecordingVoiceMessage(isCancelled = true, rootThreadEventId = getRootThreadEventId())
)
updateRecordingUiState(RecordingUiState.Idle) updateRecordingUiState(RecordingUiState.Idle)
} }
override fun onRecordingLimitReached() { override fun onRecordingLimitReached() {
messageComposerViewModel.handle( messageComposerViewModel.handle(
MessageComposerAction.PauseRecordingVoiceMessage) MessageComposerAction.PauseRecordingVoiceMessage
)
updateRecordingUiState(RecordingUiState.Draft) updateRecordingUiState(RecordingUiState.Draft)
} }
override fun onRecordingWaveformClicked() { override fun onRecordingWaveformClicked() {
messageComposerViewModel.handle( messageComposerViewModel.handle(
MessageComposerAction.PauseRecordingVoiceMessage) MessageComposerAction.PauseRecordingVoiceMessage
)
updateRecordingUiState(RecordingUiState.Draft) updateRecordingUiState(RecordingUiState.Draft)
} }
@ -827,7 +838,8 @@ class TimelineFragment @Inject constructor(
private fun updateRecordingUiState(state: RecordingUiState) { private fun updateRecordingUiState(state: RecordingUiState) {
messageComposerViewModel.handle( messageComposerViewModel.handle(
MessageComposerAction.OnVoiceRecordingUiStateChanged(state)) MessageComposerAction.OnVoiceRecordingUiStateChanged(state)
)
} }
} }
} }
@ -1527,9 +1539,11 @@ class TimelineFragment @Inject constructor(
attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@TimelineFragment) attachmentTypeSelector = AttachmentTypeSelectorView(vectorBaseActivity, vectorBaseActivity.layoutInflater, this@TimelineFragment)
attachmentTypeSelector.setAttachmentVisibility( attachmentTypeSelector.setAttachmentVisibility(
AttachmentTypeSelectorView.Type.LOCATION, AttachmentTypeSelectorView.Type.LOCATION,
vectorPreferences.isLocationSharingEnabled()) vectorPreferences.isLocationSharingEnabled()
)
attachmentTypeSelector.setAttachmentVisibility( attachmentTypeSelector.setAttachmentVisibility(
AttachmentTypeSelectorView.Type.POLL, !isThreadTimeLine()) AttachmentTypeSelectorView.Type.POLL, !isThreadTimeLine()
)
} }
attachmentTypeSelector.show(views.composerLayout.views.attachmentButton) attachmentTypeSelector.show(views.composerLayout.views.attachmentButton)
} }
@ -2292,12 +2306,18 @@ class TimelineFragment @Inject constructor(
handleCancelSend(action) handleCancelSend(action)
} }
is EventSharedAction.ReportContentSpam -> { is EventSharedAction.ReportContentSpam -> {
timelineViewModel.handle(RoomDetailAction.ReportContent( timelineViewModel.handle(
action.eventId, action.senderId, "This message is spam", spam = true)) RoomDetailAction.ReportContent(
action.eventId, action.senderId, "This message is spam", spam = true
)
)
} }
is EventSharedAction.ReportContentInappropriate -> { is EventSharedAction.ReportContentInappropriate -> {
timelineViewModel.handle(RoomDetailAction.ReportContent( timelineViewModel.handle(
action.eventId, action.senderId, "This message is inappropriate", inappropriate = true)) RoomDetailAction.ReportContent(
action.eventId, action.senderId, "This message is inappropriate", inappropriate = true
)
)
} }
is EventSharedAction.ReportContentCustom -> { is EventSharedAction.ReportContentCustom -> {
promptReasonToReportContent(action) promptReasonToReportContent(action)
@ -2443,7 +2463,8 @@ class TimelineFragment @Inject constructor(
displayName = timelineViewModel.getRoomSummary()?.displayName, displayName = timelineViewModel.getRoomSummary()?.displayName,
avatarUrl = timelineViewModel.getRoomSummary()?.avatarUrl, avatarUrl = timelineViewModel.getRoomSummary()?.avatarUrl,
roomEncryptionTrustLevel = timelineViewModel.getRoomSummary()?.roomEncryptionTrustLevel, roomEncryptionTrustLevel = timelineViewModel.getRoomSummary()?.roomEncryptionTrustLevel,
rootThreadEventId = rootThreadEventId) rootThreadEventId = rootThreadEventId
)
navigator.openThread(it, roomThreadDetailArgs) navigator.openThread(it, roomThreadDetailArgs)
} }
} }
@ -2479,7 +2500,8 @@ class TimelineFragment @Inject constructor(
roomId = timelineArgs.roomId, roomId = timelineArgs.roomId,
displayName = timelineViewModel.getRoomSummary()?.displayName, displayName = timelineViewModel.getRoomSummary()?.displayName,
roomEncryptionTrustLevel = timelineViewModel.getRoomSummary()?.roomEncryptionTrustLevel, roomEncryptionTrustLevel = timelineViewModel.getRoomSummary()?.roomEncryptionTrustLevel,
avatarUrl = timelineViewModel.getRoomSummary()?.avatarUrl) avatarUrl = timelineViewModel.getRoomSummary()?.avatarUrl
)
navigator.openThreadList(it, roomThreadDetailArgs) navigator.openThreadList(it, roomThreadDetailArgs)
} }
} }

View File

@ -24,6 +24,7 @@ import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.animations.play import im.vector.app.core.animations.play
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.utils.isAnimationEnabled
import im.vector.app.databinding.FragmentFtueAccountCreatedBinding import im.vector.app.databinding.FragmentFtueAccountCreatedBinding
import im.vector.app.features.onboarding.OnboardingAction import im.vector.app.features.onboarding.OnboardingAction
import im.vector.app.features.onboarding.OnboardingViewEvents import im.vector.app.features.onboarding.OnboardingViewEvents
@ -57,7 +58,7 @@ class FtueAuthAccountCreatedFragment @Inject constructor(
views.personalizeButtonGroup.isVisible = canPersonalize views.personalizeButtonGroup.isVisible = canPersonalize
views.takeMeHomeButtonGroup.isVisible = !canPersonalize views.takeMeHomeButtonGroup.isVisible = !canPersonalize
if (!hasPlayedConfetti && !canPersonalize) { if (!hasPlayedConfetti && !canPersonalize && requireContext().isAnimationEnabled()) {
hasPlayedConfetti = true hasPlayedConfetti = true
views.viewKonfetti.isVisible = true views.viewKonfetti.isVisible = true
views.viewKonfetti.play() views.viewKonfetti.play()

View File

@ -22,6 +22,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.core.animations.play import im.vector.app.core.animations.play
import im.vector.app.core.utils.isAnimationEnabled
import im.vector.app.databinding.FragmentFtuePersonalizationCompleteBinding import im.vector.app.databinding.FragmentFtuePersonalizationCompleteBinding
import im.vector.app.features.onboarding.OnboardingAction import im.vector.app.features.onboarding.OnboardingAction
import im.vector.app.features.onboarding.OnboardingViewEvents import im.vector.app.features.onboarding.OnboardingViewEvents
@ -43,7 +44,7 @@ class FtueAuthPersonalizationCompleteFragment @Inject constructor() : AbstractFt
private fun setupViews() { private fun setupViews() {
views.personalizationCompleteCta.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnTakeMeHome)) } views.personalizationCompleteCta.debouncedClicks { viewModel.handle(OnboardingAction.PostViewEvent(OnboardingViewEvents.OnTakeMeHome)) }
if (!hasPlayedConfetti) { if (!hasPlayedConfetti && requireContext().isAnimationEnabled()) {
hasPlayedConfetti = true hasPlayedConfetti = true
views.viewKonfetti.isVisible = true views.viewKonfetti.isVisible = true
views.viewKonfetti.play() views.viewKonfetti.play()

View File

@ -25,7 +25,7 @@ import com.tapadoo.alerter.Alerter
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.time.Clock import im.vector.app.core.time.Clock
import im.vector.app.core.utils.isAnimationDisabled import im.vector.app.core.utils.isAnimationEnabled
import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity
import im.vector.app.features.pin.PinActivity import im.vector.app.features.pin.PinActivity
import im.vector.app.features.signout.hard.SignedOutActivity import im.vector.app.features.signout.hard.SignedOutActivity
@ -218,7 +218,7 @@ class PopupAlertManager @Inject constructor(
if (!alert.isLight) { if (!alert.isLight) {
clearLightStatusBar() clearLightStatusBar()
} }
val noAnimation = !animate || isAnimationDisabled(activity) val noAnimation = !(animate && activity.isAnimationEnabled())
alert.weakCurrentActivity = WeakReference(activity) alert.weakCurrentActivity = WeakReference(activity)
val alerter = Alerter.create(activity, alert.layoutRes) val alerter = Alerter.create(activity, alert.layoutRes)

View File

@ -204,7 +204,7 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
// Important, Battery optim white listing is needed in this mode; // Important, Battery optim white listing is needed in this mode;
// Even if using foreground service with foreground notif, it stops to work // Even if using foreground service with foreground notif, it stops to work
// in doze mode for certain devices :/ // in doze mode for certain devices :/
if (!isIgnoringBatteryOptimizations(requireContext())) { if (!requireContext().isIgnoringBatteryOptimizations()) {
requestDisablingBatteryOptimization(requireActivity(), batteryStartForActivityResult) requestDisablingBatteryOptimization(requireActivity(), batteryStartForActivityResult)
} }
} }

View File

@ -54,7 +54,7 @@ class SyncStateView @JvmOverloads constructor(context: Context, attrs: Attribute
views.syncStateProgressBar.isVisible = newState is SyncState.Running && newState.afterPause views.syncStateProgressBar.isVisible = newState is SyncState.Running && newState.afterPause
if (newState == SyncState.NoNetwork) { if (newState == SyncState.NoNetwork) {
val isAirplaneModeOn = isAirplaneModeOn(context) val isAirplaneModeOn = context.isAirplaneModeOn()
views.syncStateNoNetwork.isVisible = isAirplaneModeOn.not() views.syncStateNoNetwork.isVisible = isAirplaneModeOn.not()
views.syncStateNoNetworkAirplane.isVisible = isAirplaneModeOn views.syncStateNoNetworkAirplane.isVisible = isAirplaneModeOn
} else { } else {