diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 4c8916d70..81d54f23d 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -625,6 +625,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_settings_power_ui" = "Interface animations";
 "lng_settings_power_auto" = "Save Power on Low Battery";
 "lng_settings_power_auto_about" = "Automatically disable all animations when your laptop is in a battery saving mode.";
+"lng_settings_power_turn_off" = "Please turn off Save Power on Low Battery to change these settings.";
 
 "lng_settings_cloud_password_on" = "On";
 "lng_settings_cloud_password_off" = "Off";
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
index a32758489..394c43c79 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
@@ -42,6 +42,7 @@ namespace {
 
 constexpr auto kSlowmodeValues = 7;
 constexpr auto kSuggestGigagroupThreshold = 199000;
+constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000);
 
 [[nodiscard]] auto Dependencies(PowerSaving::Flags)
 -> std::vector<std::pair<PowerSaving::Flag, PowerSaving::Flag>> {
@@ -453,8 +454,38 @@ template <typename Flags>
 	struct State final {
 		std::map<Flags, not_null<Ui::AbstractCheckView*>> checkViews;
 		rpl::event_stream<> anyChanges;
+		rpl::variable<QString> forceDisabledMessage;
+		rpl::variable<bool> forceDisabled;
+		base::flat_map<Flags, bool> realCheckedValues;
+		base::weak_ptr<Ui::Toast::Instance> toast;
 	};
 	const auto state = container->lifetime().make_state<State>();
+	if (descriptor.forceDisabledMessage) {
+		state->forceDisabledMessage = std::move(
+			descriptor.forceDisabledMessage);
+		state->forceDisabled = state->forceDisabledMessage.value(
+		) | rpl::map([=](const QString &message) {
+			return !message.isEmpty();
+		});
+
+		state->forceDisabled.value(
+		) | rpl::start_with_next([=](bool disabled) {
+			if (disabled) {
+				for (const auto &[flags, checkView] : state->checkViews) {
+					checkView->setChecked(false, anim::type::normal);
+				}
+			} else {
+				for (const auto &[flags, checkView] : state->checkViews) {
+					if (const auto i = state->realCheckedValues.find(flags)
+						; i != state->realCheckedValues.end()) {
+						checkView->setChecked(
+							i->second,
+							anim::type::normal);
+					}
+				}
+			}
+		}, container->lifetime());
+	}
 
 	const auto &st = descriptor.st ? *descriptor.st : st::rightsButton;
 	const auto value = [=] {
@@ -492,7 +523,9 @@ template <typename Flags>
 		const auto locked = (lockedIt != end(descriptor.disabledMessages))
 			? std::make_optional(lockedIt->second)
 			: std::nullopt;
-		const auto toggled = ((checked & flags) != 0);
+		const auto realChecked = (checked & flags) != 0;
+		state->realCheckedValues.emplace(flags, realChecked);
+		const auto toggled = realChecked && !state->forceDisabled.current();
 
 		const auto checkView = [&]() -> not_null<Ui::AbstractCheckView*> {
 			if (isInner) {
@@ -559,15 +592,30 @@ template <typename Flags>
 		state->checkViews.emplace(flags, checkView);
 		checkView->checkedChanges(
 		) | rpl::start_with_next([=](bool checked) {
-			if (locked.has_value()) {
-				if (checked != toggled) {
-					Ui::ShowMultilineToast({
+			if (checked && state->forceDisabled.current()) {
+				if (!state->toast) {
+					state->toast = Ui::ShowMultilineToast({
 						.parentOverride = container,
-						.text = { *locked },
+						.text = { state->forceDisabledMessage.current() },
+						.duration = kForceDisableTooltipDuration,
 					});
+				}
+				checkView->setChecked(false, anim::type::instant);
+			} else if (locked.has_value()) {
+				if (checked != toggled) {
+					if (!state->toast) {
+						state->toast = Ui::ShowMultilineToast({
+							.parentOverride = container,
+							.text = { *locked },
+							.duration = kForceDisableTooltipDuration,
+						});
+					}
 					checkView->setChecked(toggled, anim::type::instant);
 				}
 			} else {
+				if (!state->forceDisabled.current()) {
+					state->realCheckedValues[flags] = checked;
+				}
 				InvokeQueued(container, [=] {
 					applyDependencies(checkView);
 					state->anyChanges.fire({});
@@ -1141,12 +1189,15 @@ ChatAdminRights AdminRightsForOwnershipTransfer(
 
 EditFlagsControl<PowerSaving::Flags> CreateEditPowerSaving(
 		QWidget *parent,
-		PowerSaving::Flags flags) {
+		PowerSaving::Flags flags,
+		rpl::producer<QString> forceDisabledMessage) {
 	auto widget = object_ptr<Ui::VerticalLayout>(parent);
+	auto descriptor = Settings::PowerSavingLabels();
+	descriptor.forceDisabledMessage = std::move(forceDisabledMessage);
 	auto result = CreateEditFlags(
 		widget.data(),
 		flags,
-		Settings::PowerSavingLabels());
+		std::move(descriptor));
 	result.widget = std::move(widget);
 
 	return result;
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h
index 0cc1d0ed8..3e18d7b6a 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h
@@ -74,6 +74,7 @@ struct EditFlagsDescriptor {
 	std::vector<NestedEditFlagsLabels<Flags>> labels;
 	base::flat_map<Flags, QString> disabledMessages;
 	const style::SettingsButton *st = nullptr;
+	rpl::producer<QString> forceDisabledMessage;
 };
 
 using RestrictionLabel = EditFlagsLabel<ChatRestrictions>;
@@ -109,5 +110,6 @@ using AdminRightLabel = EditFlagsLabel<ChatAdminRights>;
 
 [[nodiscard]] auto CreateEditPowerSaving(
 	QWidget *parent,
-	PowerSaving::Flags flags
+	PowerSaving::Flags flags,
+	rpl::producer<QString> forceDisabledMessage
 ) -> EditFlagsControl<PowerSaving::Flags>;
diff --git a/Telegram/SourceFiles/calls/calls_top_bar.cpp b/Telegram/SourceFiles/calls/calls_top_bar.cpp
index 091cdf538..c66805f43 100644
--- a/Telegram/SourceFiles/calls/calls_top_bar.cpp
+++ b/Telegram/SourceFiles/calls/calls_top_bar.cpp
@@ -499,7 +499,7 @@ void TopBar::initBlobsUnder(
 
 	using namespace rpl::mappers;
 	auto hideBlobs = rpl::combine(
-		PowerSaving::Value(PowerSaving::kCalls),
+		PowerSaving::OnValue(PowerSaving::kCalls),
 		Core::App().appDeactivatedValue(),
 		group->instanceStateValue()
 	) | rpl::map(_1 || _2 || _3 == GroupCall::InstanceState::Disconnected);
diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp
index 7dd2e8365..10e1123cd 100644
--- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp
+++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp
@@ -235,7 +235,7 @@ Members::Controller::Controller(
 	}, _lifetime);
 
 	rpl::combine(
-		PowerSaving::Value(PowerSaving::kCalls),
+		PowerSaving::OnValue(PowerSaving::kCalls),
 		Core::App().appDeactivatedValue()
 	) | rpl::start_with_next([=](bool disabled, bool deactivated) {
 		const auto hide = disabled || deactivated;
diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp
index fb00ef4a0..b6f6b3cc3 100644
--- a/Telegram/SourceFiles/core/application.cpp
+++ b/Telegram/SourceFiles/core/application.cpp
@@ -14,10 +14,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_user.h"
 #include "data/data_channel.h"
 #include "data/data_download_manager.h"
-#include "base/timer.h"
+#include "base/battery_saving.h"
 #include "base/event_filter.h"
 #include "base/concurrent_timer.h"
 #include "base/qt_signal_producer.h"
+#include "base/timer.h"
 #include "base/unixtime.h"
 #include "core/core_settings.h"
 #include "core/update_checker.h"
@@ -78,6 +79,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/effects/animations.h"
 #include "ui/effects/spoiler_mess.h"
 #include "ui/cached_round_corners.h"
+#include "ui/power_saving.h"
 #include "storage/serialize_common.h"
 #include "storage/storage_domain.h"
 #include "storage/storage_databases.h"
@@ -143,6 +145,7 @@ Application::Application(not_null<Launcher*> launcher)
 , _launcher(launcher)
 , _private(std::make_unique<Private>())
 , _platformIntegration(Platform::Integration::Create())
+, _batterySaving(std::make_unique<base::BatterySaving>())
 , _databases(std::make_unique<Storage::Databases>())
 , _animationsManager(std::make_unique<Ui::Animations::Manager>())
 , _clearEmojiImageLoaderTimer([=] { clearEmojiSourceImages(); })
@@ -279,6 +282,13 @@ void Application::run() {
 		_mediaControlsManager = std::make_unique<MediaControlsManager>();
 	}
 
+	rpl::combine(
+		_batterySaving->value(),
+		settings().ignoreBatterySavingValue()
+	) | rpl::start_with_next([=](bool saving, bool ignore) {
+		PowerSaving::SetForceAll(saving && !ignore);
+	}, _lifetime);
+
 	style::ShortAnimationPlaying(
 	) | rpl::start_with_next([=](bool playing) {
 		if (playing) {
@@ -409,14 +419,14 @@ void Application::showOpenGLCrashNotification() {
 	const auto enable = [=] {
 		Ui::GL::ForceDisable(false);
 		Ui::GL::CrashCheckFinish();
-		Core::App().settings().setDisableOpenGL(false);
+		settings().setDisableOpenGL(false);
 		Local::writeSettings();
 		Restart();
 	};
 	const auto keepDisabled = [=] {
 		Ui::GL::ForceDisable(true);
 		Ui::GL::CrashCheckFinish();
-		Core::App().settings().setDisableOpenGL(true);
+		settings().setDisableOpenGL(true);
 		Local::writeSettings();
 	};
 	_lastActivePrimaryWindow->show(Ui::MakeConfirmBox({
@@ -658,6 +668,10 @@ Settings &Application::settings() {
 	return _private->settings;
 }
 
+const Settings &Application::settings() const {
+	return _private->settings;
+}
+
 void Application::saveSettingsDelayed(crl::time delay) {
 	if (_saveSettingsTimer) {
 		_saveSettingsTimer->callOnce(delay);
@@ -670,7 +684,7 @@ void Application::saveSettings() {
 
 bool Application::canReadDefaultDownloadPath(bool always) const {
 	if (KSandbox::isInside()
-		&& (always || Core::App().settings().downloadPath().isEmpty())) {
+		&& (always || settings().downloadPath().isEmpty())) {
 		const auto path = QStandardPaths::writableLocation(
 			QStandardPaths::DownloadLocation);
 		return base::CanReadDirectory(path);
@@ -679,7 +693,7 @@ bool Application::canReadDefaultDownloadPath(bool always) const {
 }
 
 bool Application::canSaveFileWithoutAskingForPath() const {
-	return !Core::App().settings().askDownloadPath();
+	return !settings().askDownloadPath();
 }
 
 MTP::Config &Application::fallbackProductionConfig() const {
diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h
index a69809375..963275e74 100644
--- a/Telegram/SourceFiles/core/application.h
+++ b/Telegram/SourceFiles/core/application.h
@@ -13,6 +13,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 class History;
 
+namespace base {
+class BatterySaving;
+} // namespace base
+
 namespace Platform {
 class Integration;
 } // namespace Platform
@@ -127,15 +131,14 @@ public:
 	Application &operator=(const Application &other) = delete;
 	~Application();
 
+	void run();
+
 	[[nodiscard]] Launcher &launcher() const {
 		return *_launcher;
 	}
 	[[nodiscard]] Platform::Integration &platformIntegration() const {
 		return *_platformIntegration;
 	}
-
-	void run();
-
 	[[nodiscard]] Ui::Animations::Manager &animationManager() const {
 		return *_animationsManager;
 	}
@@ -150,6 +153,9 @@ public:
 	[[nodiscard]] Tray &tray() const {
 		return *_tray;
 	}
+	[[nodiscard]] base::BatterySaving &batterySaving() const {
+		return *_batterySaving;
+	}
 
 	// Windows interface.
 	bool hasActiveWindow(not_null<Main::Session*> session) const;
@@ -191,6 +197,7 @@ public:
 
 	void startSettingsAndBackground();
 	[[nodiscard]] Settings &settings();
+	[[nodiscard]] const Settings &settings() const;
 	void saveSettingsDelayed(crl::time delay = kDefaultSaveDelay);
 	void saveSettings();
 
@@ -373,6 +380,7 @@ private:
 	struct Private;
 	const std::unique_ptr<Private> _private;
 	const std::unique_ptr<Platform::Integration> _platformIntegration;
+	const std::unique_ptr<base::BatterySaving> _batterySaving;
 
 	const std::unique_ptr<Storage::Databases> _databases;
 	const std::unique_ptr<Ui::Animations::Manager> _animationsManager;
diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp
index 79e560f8e..442102968 100644
--- a/Telegram/SourceFiles/core/core_settings.cpp
+++ b/Telegram/SourceFiles/core/core_settings.cpp
@@ -197,7 +197,8 @@ QByteArray Settings::serialize() const {
 		+ sizeof(qint32)
 		+ sizeof(quint64)
 		+ sizeof(qint32) * 3
-		+ Serialize::bytearraySize(mediaViewPosition);
+		+ Serialize::bytearraySize(mediaViewPosition)
+		+ sizeof(qint32);
 
 	auto result = QByteArray();
 	result.reserve(size);
@@ -329,7 +330,8 @@ QByteArray Settings::serialize() const {
 			<< qint32(_windowTitleContent.current().hideChatName ? 1 : 0)
 			<< qint32(_windowTitleContent.current().hideAccountName ? 1 : 0)
 			<< qint32(_windowTitleContent.current().hideTotalUnread ? 1 : 0)
-			<< mediaViewPosition;
+			<< mediaViewPosition
+			<< qint32(_ignoreBatterySaving.current() ? 1 : 0);
 	}
 	return result;
 }
@@ -433,6 +435,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
 	qint32 hideAccountName = _windowTitleContent.current().hideAccountName ? 1 : 0;
 	qint32 hideTotalUnread = _windowTitleContent.current().hideTotalUnread ? 1 : 0;
 	QByteArray mediaViewPosition;
+	qint32 ignoreBatterySaving = _ignoreBatterySaving.current() ? 1 : 0;
 
 	stream >> themesAccentColors;
 	if (!stream.atEnd()) {
@@ -661,6 +664,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
 	if (!stream.atEnd()) {
 		stream >> mediaViewPosition;
 	}
+	if (!stream.atEnd()) {
+		stream >> ignoreBatterySaving;
+	}
 	if (stream.status() != QDataStream::Ok) {
 		LOG(("App Error: "
 			"Bad data for Core::Settings::constructFromSerialized()"));
@@ -857,6 +863,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) {
 			_mediaViewPosition = { .maximized = 2 };
 		}
 	}
+	_ignoreBatterySaving = (ignoreBatterySaving == 1);
 }
 
 QString Settings::getSoundPath(const QString &key) const {
diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h
index b65fd3118..eb3df53fd 100644
--- a/Telegram/SourceFiles/core/core_settings.h
+++ b/Telegram/SourceFiles/core/core_settings.h
@@ -777,6 +777,15 @@ public:
 	void setMediaViewPosition(const WindowPosition &position) {
 		_mediaViewPosition = position;
 	}
+	[[nodiscard]] bool ignoreBatterySaving() const {
+		return _ignoreBatterySaving.current();
+	}
+	[[nodiscard]] rpl::producer<bool> ignoreBatterySavingValue() const {
+		return _ignoreBatterySaving.value();
+	}
+	void setIgnoreBatterySavingValue(bool value) {
+		_ignoreBatterySaving = value;
+	}
 
 	[[nodiscard]] static bool ThirdColumnByDefault();
 	[[nodiscard]] static float64 DefaultDialogsWidthRatio();
@@ -900,6 +909,7 @@ private:
 	rpl::event_stream<> _skipTranslationLanguagesChanges;
 	bool _rememberedDeleteMessageOnlyForYou = false;
 	WindowPosition _mediaViewPosition = { .maximized = 2 };
+	rpl::variable<bool> _ignoreBatterySaving = false;
 
 	bool _tabbedReplacedWithInfo = false; // per-window
 	rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window
diff --git a/Telegram/SourceFiles/settings/settings_power_saving.cpp b/Telegram/SourceFiles/settings/settings_power_saving.cpp
index bd75ebbf7..dd4561aed 100644
--- a/Telegram/SourceFiles/settings/settings_power_saving.cpp
+++ b/Telegram/SourceFiles/settings/settings_power_saving.cpp
@@ -7,11 +7,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "settings/settings_power_saving.h"
 
+#include "base/battery_saving.h"
 #include "boxes/peers/edit_peer_permissions_box.h"
 #include "core/application.h"
+#include "core/core_settings.h"
 #include "lang/lang_keys.h"
 #include "settings/settings_common.h"
 #include "ui/layers/generic_box.h"
+#include "ui/toasts/common_toasts.h"
 #include "ui/widgets/buttons.h"
 #include "ui/power_saving.h"
 #include "styles/style_menu_icons.h"
@@ -19,6 +22,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "styles/style_settings.h"
 
 namespace Settings {
+namespace {
+
+constexpr auto kForceDisableTooltipDuration = 3 * crl::time(1000);
+
+} // namespace
 
 void PowerSavingBox(not_null<Ui::GenericBox*> box) {
 	box->setStyle(st::layerBox);
@@ -26,40 +34,90 @@ void PowerSavingBox(not_null<Ui::GenericBox*> box) {
 	box->setWidth(st::boxWideWidth);
 
 	const auto container = box->verticalLayout();
+	const auto ignore = Core::App().settings().ignoreBatterySaving();
+	const auto batterySaving = Core::App().batterySaving().enabled();
 
 	// Force top shadow visibility.
 	box->setPinnedToTopContent(
 		object_ptr<Ui::FixedHeightWidget>(box, st::lineWidth));
 
-	AddSubsectionTitle(
+	const auto subtitle = AddSubsectionTitle(
 		container,
 		tr::lng_settings_power_subtitle(),
 		st::powerSavingSubtitlePadding);
 
+	struct State {
+		rpl::variable<QString> forceDisabledMessage;
+	};
+	const auto state = container->lifetime().make_state<State>();
+	state->forceDisabledMessage = (batterySaving.value_or(false) && !ignore)
+		? tr::lng_settings_power_turn_off(tr::now)
+		: QString();
+
 	auto [checkboxes, getResult, changes] = CreateEditPowerSaving(
 		box,
-		PowerSaving::kAll & ~PowerSaving::Current());
+		PowerSaving::kAll & ~PowerSaving::Current(),
+		state->forceDisabledMessage.value());
 
+	const auto controlsRaw = checkboxes.data();
 	box->addRow(std::move(checkboxes), {});
 
 	auto automatic = (Ui::SettingsButton*)nullptr;
-	const auto hasBattery = true;
-	const auto automaticEnabled = true;
-	if (hasBattery) {
+	if (batterySaving.has_value()) {
 		AddSkip(container);
 		AddDivider(container);
 		AddSkip(container);
-		AddButton(
+		automatic = AddButton(
 			container,
 			tr::lng_settings_power_auto(),
 			st::powerSavingButtonNoIcon
-		)->toggleOn(rpl::single(automaticEnabled));
+		)->toggleOn(rpl::single(!ignore));
 		AddSkip(container);
 		AddDividerText(container, tr::lng_settings_power_auto_about());
+
+		state->forceDisabledMessage = rpl::combine(
+			automatic->toggledValue(),
+			Core::App().batterySaving().value()
+		) | rpl::map([=](bool dontIgnore, bool saving) {
+			return (saving && dontIgnore)
+				? tr::lng_settings_power_turn_off()
+				: rpl::single(QString());
+		}) | rpl::flatten_latest();
+
+		const auto disabler = Ui::CreateChild<Ui::AbstractButton>(container.get());
+		disabler->setClickedCallback([=] {
+			Ui::ShowMultilineToast({
+				.parentOverride = container,
+				.text = tr::lng_settings_power_turn_off(tr::now),
+				.duration = kForceDisableTooltipDuration,
+			});
+		});
+		disabler->paintRequest() | rpl::start_with_next([=](QRect clip) {
+			auto color = st::boxBg->c;
+			color.setAlpha(96);
+			QPainter(disabler).fillRect(clip, color);
+		}, disabler->lifetime());
+		rpl::combine(
+			subtitle->geometryValue(),
+			controlsRaw->geometryValue()
+		) | rpl::start_with_next([=](QRect subtitle, QRect controls) {
+			disabler->setGeometry(subtitle.united(controls));
+		}, disabler->lifetime());
+		disabler->showOn(state->forceDisabledMessage.value(
+		) | rpl::map([=](const QString &value) {
+			return !value.isEmpty();
+		}));
 	}
 
 	box->addButton(tr::lng_settings_save(), [=, collect = getResult] {
-		Set(PowerSaving::kAll & ~collect());
+		const auto ignore = automatic
+			? !automatic->toggled()
+			: Core::App().settings().ignoreBatterySaving();
+		const auto batterySaving = Core::App().batterySaving().enabled();
+		if (ignore || !batterySaving.value_or(false)) {
+			Set(PowerSaving::kAll & ~collect());
+		}
+		Core::App().settings().setIgnoreBatterySavingValue(ignore);
 		Core::App().saveSettingsDelayed();
 		box->closeBox();
 	});
diff --git a/Telegram/SourceFiles/ui/chat/group_call_userpics.cpp b/Telegram/SourceFiles/ui/chat/group_call_userpics.cpp
index ffa961ea2..a88f2074d 100644
--- a/Telegram/SourceFiles/ui/chat/group_call_userpics.cpp
+++ b/Telegram/SourceFiles/ui/chat/group_call_userpics.cpp
@@ -117,7 +117,7 @@ GroupCallUserpics::GroupCallUserpics(
 	});
 
 	rpl::combine(
-		PowerSaving::Value(PowerSaving::kCalls),
+		PowerSaving::OnValue(PowerSaving::kCalls),
 		std::move(hideBlobs)
 	) | rpl::start_with_next([=](bool disabled, bool deactivated) {
 		const auto hide = disabled || deactivated;
diff --git a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp
index 61b33edd2..64be6e5c2 100644
--- a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp
+++ b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp
@@ -517,7 +517,7 @@ CallMuteButton::CallMuteButton(
 	parent,
 	_st->active.bgSize,
 	rpl::combine(
-		PowerSaving::Value(PowerSaving::kCalls),
+		PowerSaving::OnValue(PowerSaving::kCalls),
 		std::move(hideBlobs),
 		_state.value(
 		) | rpl::map([](const CallMuteButtonState &state) {
diff --git a/Telegram/SourceFiles/ui/power_saving.cpp b/Telegram/SourceFiles/ui/power_saving.cpp
index 32d8b81fc..a401aca8a 100644
--- a/Telegram/SourceFiles/ui/power_saving.cpp
+++ b/Telegram/SourceFiles/ui/power_saving.cpp
@@ -12,16 +12,32 @@ namespace {
 
 Flags Data/* = {}*/;
 rpl::event_stream<> Events;
+bool AllForced/* = false*/;
 
 } // namespace
 
+void Set(Flags flags) {
+	if (const auto diff = Data ^ flags) {
+		Data = flags;
+		if (!AllForced) {
+			if (diff & kAnimations) {
+				anim::SetDisabled(On(kAnimations));
+			}
+			Events.fire({});
+		}
+	}
+}
+
 Flags Current() {
 	return Data;
 }
 
-void Set(Flags flags) {
-	if (const auto diff = Data ^ flags) {
-		Data = flags;
+void SetForceAll(bool force) {
+	if (AllForced == force) {
+		return;
+	}
+	AllForced = force;
+	if (const auto diff = Data ^ kAll) {
 		if (diff & kAnimations) {
 			anim::SetDisabled(On(kAnimations));
 		}
@@ -29,18 +45,12 @@ void Set(Flags flags) {
 	}
 }
 
-rpl::producer<Flags> Changes() {
-	return Events.events() | rpl::map(Current);
+bool ForceAll() {
+	return AllForced;
 }
 
-rpl::producer<Flags> Value() {
-	return rpl::single(Current()) | rpl::then(Changes());
-}
-
-rpl::producer<bool> Value(Flag flag) {
-	return Value() | rpl::map([=](Flags flags) {
-		return (flags & flag) != 0;
-	}) | rpl::distinct_until_changed();
+rpl::producer<> Changes() {
+	return Events.events();
 }
 
 } // namespace PowerSaving
diff --git a/Telegram/SourceFiles/ui/power_saving.h b/Telegram/SourceFiles/ui/power_saving.h
index 003171cbe..c3b065375 100644
--- a/Telegram/SourceFiles/ui/power_saving.h
+++ b/Telegram/SourceFiles/ui/power_saving.h
@@ -25,14 +25,21 @@ enum Flag : uint32 {
 inline constexpr bool is_flag_type(Flag) { return true; }
 using Flags = base::flags<Flag>;
 
-[[nodiscard]] Flags Current();
-[[nodiscard]] rpl::producer<Flags> Changes();
-[[nodiscard]] rpl::producer<Flags> Value();
-[[nodiscard]] rpl::producer<bool> Value(Flag flag);
 void Set(Flags flags);
+[[nodiscard]] Flags Current();
+
+void SetForceAll(bool force);
+[[nodiscard]] bool ForceAll();
+
+[[nodiscard]] rpl::producer<> Changes();
 
 [[nodiscard]] inline bool On(Flag flag) {
-	return Current() & flag;
+	return ForceAll() || (Current() & flag);
+}
+[[nodiscard]] inline rpl::producer<bool> OnValue(Flag flag) {
+	return rpl::single(On(flag)) | rpl::then(Changes() | rpl::map([=] {
+		return On(flag);
+	})) | rpl::distinct_until_changed();
 }
 
 } // namespace PowerSaving
diff --git a/Telegram/lib_base b/Telegram/lib_base
index 2e306a724..17ac5644d 160000
--- a/Telegram/lib_base
+++ b/Telegram/lib_base
@@ -1 +1 @@
-Subproject commit 2e306a7245de70b6e6943b0bc33892bf0d327320
+Subproject commit 17ac5644d1f5cdaeb79b42cdf931b819cf0dbcf3
diff --git a/Telegram/lib_rpl b/Telegram/lib_rpl
index bc7b4cae2..8b1015d1b 160000
--- a/Telegram/lib_rpl
+++ b/Telegram/lib_rpl
@@ -1 +1 @@
-Subproject commit bc7b4cae2ea69c67a7b501289ffba7c8eddc5d19
+Subproject commit 8b1015d1bd57ef03fcd07a3eeddd3f5a9b688ade