| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/ambient/ambient_controller.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/ambient/ambient_constants.h" |
| #include "ash/ambient/ambient_managed_slideshow_ui_launcher.h" |
| #include "ash/ambient/ambient_ui_launcher.h" |
| #include "ash/ambient/ambient_ui_settings.h" |
| #include "ash/ambient/ambient_video_ui_launcher.h" |
| #include "ash/ambient/ambient_weather_controller.h" |
| #include "ash/ambient/metrics/ambient_multi_screen_metrics_recorder.h" |
| #include "ash/ambient/model/ambient_animation_photo_config.h" |
| #include "ash/ambient/model/ambient_backend_model_observer.h" |
| #include "ash/ambient/model/ambient_slideshow_photo_config.h" |
| #include "ash/ambient/model/ambient_topic_queue_animation_delegate.h" |
| #include "ash/ambient/model/ambient_topic_queue_slideshow_delegate.h" |
| #include "ash/ambient/resources/ambient_animation_static_resources.h" |
| #include "ash/ambient/ui/ambient_animation_frame_rate_controller.h" |
| #include "ash/ambient/ui/ambient_animation_progress_tracker.h" |
| #include "ash/ambient/ui/ambient_container_view.h" |
| #include "ash/ambient/ui/ambient_view_delegate.h" |
| #include "ash/ambient/util/ambient_util.h" |
| #include "ash/assistant/model/assistant_interaction_model.h" |
| #include "ash/constants/ambient_theme.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/login/ui/lock_screen.h" |
| #include "ash/public/cpp/ambient/ambient_backend_controller.h" |
| #include "ash/public/cpp/ambient/ambient_client.h" |
| #include "ash/public/cpp/ambient/ambient_metrics.h" |
| #include "ash/public/cpp/ambient/ambient_prefs.h" |
| #include "ash/public/cpp/ambient/ambient_ui_model.h" |
| #include "ash/public/cpp/ambient/common/ambient_settings.h" |
| #include "ash/public/cpp/ambient/fake_ambient_backend_controller_impl.h" |
| #include "ash/public/cpp/assistant/controller/assistant_interaction_controller.h" |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shell.h" |
| #include "ash/strings/grit/ash_strings.h" |
| #include "ash/system/power/power_status.h" |
| #include "base/check.h" |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/logging.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/path_service.h" |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "base/values.h" |
| #include "build/buildflag.h" |
| #include "cc/paint/skottie_wrapper.h" |
| #include "chromeos/ash/components/assistant/buildflags.h" |
| #include "chromeos/ash/services/assistant/public/cpp/assistant_service.h" |
| #include "chromeos/dbus/power/power_manager_client.h" |
| #include "chromeos/dbus/power_manager/backlight.pb.h" |
| #include "chromeos/dbus/power_manager/idle.pb.h" |
| #include "components/prefs/pref_change_registrar.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/ui_base_types.h" |
| #include "ui/base/user_activity/user_activity_detector.h" |
| #include "ui/views/accessibility/view_accessibility.h" |
| #include "ui/views/view.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_delegate.h" |
| #include "ui/wm/core/cursor_manager.h" |
| #include "ui/wm/core/visibility_controller.h" |
| #include "ui/wm/core/window_animations.h" |
| |
| #if BUILDFLAG(ENABLE_CROS_AMBIENT_MODE_BACKEND) |
| #include "ash/ambient/backdrop/ambient_backend_controller_impl.h" |
| #endif // BUILDFLAG(ENABLE_CROS_AMBIENT_MODE_BACKEND) |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Used by wake lock APIs. |
| constexpr char kWakeLockReason[] = "AmbientMode"; |
| |
| std::unique_ptr<AmbientBackendController> CreateAmbientBackendController() { |
| #if BUILDFLAG(ENABLE_CROS_AMBIENT_MODE_BACKEND) |
| return std::make_unique<AmbientBackendControllerImpl>(); |
| #else |
| return std::make_unique<FakeAmbientBackendControllerImpl>(); |
| #endif // BUILDFLAG(ENABLE_CROS_AMBIENT_MODE_BACKEND) |
| } |
| |
| // Returns the name of the ambient widget. |
| std::string GetWidgetName() { |
| if (ambient::util::IsShowing(LockScreen::ScreenType::kLock)) |
| return "LockScreenAmbientModeContainer"; |
| return "InSessionAmbientModeContainer"; |
| } |
| |
| // Returns true if the device is currently connected to a charger. |
| bool IsChargerConnected() { |
| DCHECK(PowerStatus::IsInitialized()); |
| auto* power_status = PowerStatus::Get(); |
| if (power_status->IsBatteryPresent()) { |
| // If battery is full or battery is charging, that implies power is |
| // connected. Also return true if a power source is connected and |
| // battery is not discharging. |
| return power_status->IsBatteryCharging() || |
| (power_status->IsLinePowerConnected() && |
| power_status->GetBatteryPercent() > 95.f); |
| } else { |
| // Chromeboxes have no battery. |
| return power_status->IsLinePowerConnected(); |
| } |
| } |
| |
| bool IsUiHidden(AmbientUiVisibility visibility) { |
| return visibility == AmbientUiVisibility::kHidden; |
| } |
| |
| PrefService* GetPrimaryUserPrefService() { |
| return Shell::Get()->session_controller()->GetPrimaryUserPrefService(); |
| } |
| |
| bool IsUserAmbientModeEnabled() { |
| if (!AmbientClient::Get()->IsAmbientModeAllowed()) { |
| return false; |
| } |
| |
| auto* pref_service = GetPrimaryUserPrefService(); |
| return pref_service && |
| pref_service->GetBoolean(ambient::prefs::kAmbientModeEnabled); |
| } |
| |
| bool IsAmbientModeAllowed() { |
| return ash::features::IsAmbientModeManagedScreensaverEnabled() || |
| AmbientClient::Get()->IsAmbientModeAllowed(); |
| } |
| |
| bool IsAmbientModeManagedScreensaverEnabled() { |
| // TODO(fahadmansoor): Consider adding additional client side checks to |
| // keep behavior consistent with the consumer ambient mode. |
| auto* pref_service = GetPrimaryUserPrefService(); |
| return ash::features::IsAmbientModeManagedScreensaverEnabled() && |
| pref_service && |
| pref_service->GetBoolean( |
| ambient::prefs::kAmbientModeManagedScreensaverEnabled); |
| } |
| |
| bool IsAmbientModeEnabled() { |
| return IsUserAmbientModeEnabled() || IsAmbientModeManagedScreensaverEnabled(); |
| } |
| |
| // Get the cache root path for ambient mode. |
| base::FilePath GetCacheRootPath() { |
| base::FilePath home_dir; |
| CHECK(base::PathService::Get(base::DIR_HOME, &home_dir)); |
| return home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName)); |
| } |
| |
| class AmbientWidgetDelegate : public views::WidgetDelegate { |
| public: |
| AmbientWidgetDelegate() { |
| SetCanMaximize(true); |
| SetOwnedByWidget(true); |
| } |
| }; |
| |
| } // namespace |
| |
| // static |
| void AmbientController::RegisterProfilePrefs(PrefRegistrySimple* registry) { |
| registry->RegisterStringPref(ash::ambient::prefs::kAmbientBackdropClientId, |
| std::string()); |
| |
| // Do not sync across devices to allow different usages for different |
| // devices. |
| registry->RegisterBooleanPref(ash::ambient::prefs::kAmbientModeEnabled, |
| false); |
| |
| // Used to upload usage metrics. Derived from |AmbientSettings| when |
| // settings are successfully saved by the user. This pref is not displayed |
| // to the user. |
| registry->RegisterIntegerPref( |
| ash::ambient::prefs::kAmbientModePhotoSourcePref, |
| static_cast<int>(ash::ambient::AmbientModePhotoSource::kUnset)); |
| |
| // Used to control the number of seconds of inactivity on lock screen before |
| // showing Ambient mode. This pref is not displayed to the user. Registered |
| // as integer rather than TimeDelta to work with prefs_util. |
| registry->RegisterIntegerPref( |
| ambient::prefs::kAmbientModeLockScreenInactivityTimeoutSeconds, |
| kLockScreenInactivityTimeout.InSeconds()); |
| |
| // Used to control the number of seconds to lock the session after starting |
| // Ambient mode. This pref is not displayed to the user. Registered as |
| // integer rather than TimeDelta to work with prefs_util. |
| registry->RegisterIntegerPref( |
| ambient::prefs::kAmbientModeLockScreenBackgroundTimeoutSeconds, |
| kLockScreenBackgroundTimeout.InSeconds()); |
| |
| // Used to control the photo refresh interval in Ambient mode. This pref is |
| // not displayed to the user. Registered as integer rather than TimeDelta to |
| // work with prefs_util. |
| registry->RegisterIntegerPref( |
| ambient::prefs::kAmbientModePhotoRefreshIntervalSeconds, |
| kPhotoRefreshInterval.InSeconds()); |
| |
| // |ambient::prefs::kAmbientTheme| is for legacy purposes only. It is being |
| // migrated to |ambient::prefs::kAmbientUiSettings|, which is the newer |
| // version of these settings. |
| registry->RegisterIntegerPref(ambient::prefs::kAmbientTheme, |
| static_cast<int>(kDefaultAmbientTheme)); |
| registry->RegisterDictionaryPref(ambient::prefs::kAmbientUiSettings, |
| base::Value::Dict()); |
| |
| registry->RegisterDoublePref( |
| ambient::prefs::kAmbientModeAnimationPlaybackSpeed, |
| kAnimationPlaybackSpeed); |
| |
| registry->RegisterBooleanPref( |
| ash::ambient::prefs::kAmbientModeManagedScreensaverEnabled, false); |
| |
| registry->RegisterIntegerPref( |
| ambient::prefs::kAmbientModeManagedScreensaverIdleTimeoutSeconds, |
| kManagedScreensaverInactivityTimeout.InSeconds()); |
| |
| registry->RegisterIntegerPref( |
| ambient::prefs::kAmbientModeManagedScreensaverImageDisplayIntervalSeconds, |
| kManagedScreensaverImageRefreshInterval.InSeconds()); |
| } |
| |
| AmbientController::AmbientController( |
| mojo::PendingRemote<device::mojom::Fingerprint> fingerprint) |
| : ambient_weather_controller_(std::make_unique<AmbientWeatherController>()), |
| fingerprint_(std::move(fingerprint)) { |
| ambient_backend_controller_ = CreateAmbientBackendController(); |
| |
| // |SessionController| is initialized before |this| in Shell. Necessary to |
| // bind observer here to monitor |OnActiveUserPrefServiceChanged|. |
| session_observer_.Observe(Shell::Get()->session_controller()); |
| backlights_forced_off_observation_.Observe( |
| Shell::Get()->backlights_forced_off_setter()); |
| } |
| |
| AmbientController::~AmbientController() { |
| CloseUi(/*immediately=*/true); |
| } |
| |
| void AmbientController::OnAmbientUiVisibilityChanged( |
| AmbientUiVisibility visibility) { |
| switch (visibility) { |
| case AmbientUiVisibility::kShown: |
| // Record metrics on ambient mode usage. |
| ambient::RecordAmbientModeActivation( |
| /*ui_mode=*/LockScreen::HasInstance() ? AmbientUiMode::kLockScreenUi |
| : AmbientUiMode::kInSessionUi, |
| /*tablet_mode=*/Shell::Get()->IsInTabletMode()); |
| |
| DCHECK(!start_time_); |
| start_time_ = base::Time::Now(); |
| |
| // Cancels the timer upon shown. |
| inactivity_timer_.Stop(); |
| |
| if (IsChargerConnected()) { |
| // Requires wake lock to prevent display from sleeping. |
| AcquireWakeLock(); |
| } |
| // Observes the |PowerStatus| on the battery charging status change for |
| // the current ambient session. |
| if (!power_status_observer_.IsObserving()) |
| power_status_observer_.Observe(PowerStatus::Get()); |
| |
| MaybeStartScreenSaver(); |
| break; |
| case AmbientUiVisibility::kPreview: { |
| MaybeStartScreenSaver(); |
| break; |
| } |
| case AmbientUiVisibility::kHidden: |
| case AmbientUiVisibility::kClosed: { |
| bool ambient_ui_was_rendering = |
| Shell::GetPrimaryRootWindowController()->HasAmbientWidget(); |
| |
| // TODO(wutao): This will clear the image cache currently. It will not |
| // work with `kHidden` if the token has expired and ambient mode is shown |
| // again. |
| StopScreensaver(); |
| |
| // Should do nothing if the wake lock has already been released. |
| ReleaseWakeLock(); |
| |
| Shell::Get()->RemovePreTargetHandler(this); |
| |
| // Should stop observing AssistantInteractionModel when ambient screen is |
| // not shown. |
| AssistantInteractionController::Get()->GetModel()->RemoveObserver(this); |
| |
| frame_rate_controller_.reset(); |
| multi_screen_metrics_recorder_.reset(); |
| |
| // |start_time_| may be empty in case of |AmbientUiVisibility::kHidden| if |
| // ambient mode has just started. |
| if (start_time_) { |
| auto elapsed = base::Time::Now() - start_time_.value(); |
| AmbientTheme theme = GetCurrentUiSettings().theme(); |
| DVLOG(2) << "Exit ambient mode. Elapsed time: " << elapsed; |
| ambient::RecordAmbientModeTimeElapsed( |
| elapsed, Shell::Get()->IsInTabletMode(), theme); |
| |
| if (!ambient_ui_was_rendering && |
| elapsed >= ambient::kMetricsStartupTimeMax) { |
| LOG(ERROR) << "Ambient UI completely failed to start"; |
| ambient::RecordAmbientModeStartupTime(elapsed, theme); |
| } |
| |
| start_time_.reset(); |
| } |
| |
| if (visibility == AmbientUiVisibility::kHidden) { |
| if (LockScreen::HasInstance()) { |
| // Add observer for user activity. |
| if (!user_activity_observer_.IsObserving()) |
| user_activity_observer_.Observe(ui::UserActivityDetector::Get()); |
| |
| // Start timer to show ambient mode. |
| inactivity_timer_.Start( |
| FROM_HERE, ambient_ui_model_.lock_screen_inactivity_timeout(), |
| base::BindOnce(&AmbientController::OnAutoShowTimeOut, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| } else { |
| DCHECK(visibility == AmbientUiVisibility::kClosed); |
| inactivity_timer_.Stop(); |
| user_activity_observer_.Reset(); |
| power_status_observer_.Reset(); |
| } |
| |
| break; |
| } |
| } |
| } |
| |
| void AmbientController::OnAutoShowTimeOut() { |
| DCHECK(IsUiHidden(ambient_ui_model_.ui_visibility())); |
| |
| // Show ambient screen after time out. |
| ShowUi(); |
| } |
| |
| void AmbientController::OnLockStateChanged(bool locked) { |
| if (!locked) { |
| // Ambient screen will be destroyed along with the lock screen when user |
| // logs in. |
| CloseUi(); |
| return; |
| } |
| |
| if (!IsAmbientModeEnabled()) { |
| VLOG(1) << "Ambient mode is not allowed."; |
| return; |
| } |
| |
| // Reset image failures to allow retrying ambient mode after lock state |
| // changes. |
| if (GetAmbientBackendModel()) { |
| GetAmbientBackendModel()->ResetImageFailures(); |
| } |
| |
| // We have 3 options to manage the token for lock screen. Here use option 1. |
| // 1. Request only one time after entering lock screen. We will use it once |
| // to request all the image links and no more requests. |
| // 2. Request one time before entering lock screen. This will introduce |
| // extra latency. |
| // 3. Request and refresh the token in the background (even the ambient mode |
| // is not started) with extra buffer time to use. When entering |
| // lock screen, it will be most likely to have the token already and |
| // enough time to use. More specifically, |
| // 3a. We will leave enough buffer time (e.g. 10 mins before expire) to |
| // start to refresh the token. |
| // 3b. When lock screen is triggered, most likely we will have >10 mins |
| // of token which can be used on lock screen. |
| // 3c. There is a corner case that we may not have the token fetched when |
| // locking screen, we probably can use PrepareForLock(callback) when |
| // locking screen. We can add the refresh token into it. If the token |
| // has already been fetched, then there is not additional time to |
| // wait. |
| RequestAccessToken(base::DoNothing(), /*may_refresh_token_on_lock=*/true); |
| |
| if (!IsShown()) { |
| // When lock screen starts, we don't immediately show the UI. The Ui is |
| // hidden and will show after a delay. |
| ShowHiddenUi(); |
| } |
| } |
| |
| void AmbientController::OnActiveUserPrefServiceChanged( |
| PrefService* pref_service) { |
| if (!IsAmbientModeAllowed() || GetPrimaryUserPrefService() != pref_service) { |
| return; |
| } |
| |
| // Do not continue if pref_change_registrar has already been set up. This |
| // prevents re-binding observers when secondary profiles are activated. |
| if (pref_change_registrar_) |
| return; |
| |
| pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>(); |
| pref_change_registrar_->Init(pref_service); |
| |
| pref_change_registrar_->Add( |
| ambient::prefs::kAmbientModeEnabled, |
| base::BindRepeating(&AmbientController::OnEnabledPrefChanged, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| OnEnabledPrefChanged(); |
| |
| if (ash::features::IsAmbientModeManagedScreensaverEnabled()) { |
| pref_change_registrar_->Add( |
| ambient::prefs::kAmbientModeManagedScreensaverEnabled, |
| base::BindRepeating( |
| &AmbientController::OnManagedScreensaverEnabledPrefChanged, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| OnManagedScreensaverEnabledPrefChanged(); |
| } |
| } |
| |
| void AmbientController::OnPowerStatusChanged() { |
| if (ambient_ui_model_.ui_visibility() != AmbientUiVisibility::kShown) { |
| // No action needed if ambient screen is not shown. |
| return; |
| } |
| |
| if (IsChargerConnected()) { |
| AcquireWakeLock(); |
| } else { |
| ReleaseWakeLock(); |
| } |
| } |
| |
| void AmbientController::ScreenIdleStateChanged( |
| const power_manager::ScreenIdleState& idle_state) { |
| DVLOG(1) << "ScreenIdleStateChanged: dimmed(" << idle_state.dimmed() |
| << ") off(" << idle_state.off() << ")"; |
| |
| if (!IsAmbientModeEnabled()) |
| return; |
| |
| is_screen_off_ = idle_state.off(); |
| |
| if (idle_state.off()) { |
| DVLOG(1) << "Screen is off, close ambient mode."; |
| |
| CloseUi(/*immediately=*/true); |
| return; |
| } |
| |
| if (idle_state.dimmed()) { |
| // Do not show the UI if lockscreen is active. The inactivity monitor should |
| // have activated ambient mode. |
| if (LockScreen::HasInstance()) |
| return; |
| |
| // Do not show UI if loading images was unsuccessful. |
| if (GetAmbientBackendModel() && |
| GetAmbientBackendModel()->ImageLoadingFailed()) { |
| VLOG(1) << "Skipping ambient mode activation due to prior failure"; |
| GetAmbientBackendModel()->ResetImageFailures(); |
| return; |
| } |
| |
| ShowUi(); |
| return; |
| } |
| |
| if (LockScreen::HasInstance() && |
| ambient_ui_model_.ui_visibility() == AmbientUiVisibility::kClosed) { |
| // Restart hidden ui if the screen is back on and lockscreen is shown. |
| ShowHiddenUi(); |
| } |
| } |
| |
| void AmbientController::OnBacklightsForcedOffChanged(bool forced_off) { |
| if (forced_off) { |
| CloseUi(/*immediately=*/true); |
| } |
| if (!forced_off && LockScreen::HasInstance() && |
| ambient_ui_model_.ui_visibility() == AmbientUiVisibility::kClosed) { |
| // Restart hidden ui if the screen is back on and lockscreen is shown. |
| ShowHiddenUi(); |
| } |
| } |
| |
| void AmbientController::SuspendImminent( |
| power_manager::SuspendImminent::Reason reason) { |
| // If about to suspend, turn everything off. This covers: |
| // 1. Clicking power button. |
| // 2. Close lid. |
| // Need to specially close the widget immediately here to be able to close |
| // the UI before device goes to suspend. Otherwise when opening lid after |
| // lid closed, there may be a flash of the old window before previous |
| // closing finished. |
| CloseUi(/*immediately=*/true); |
| is_suspend_imminent_ = true; |
| } |
| |
| void AmbientController::SuspendDone(base::TimeDelta sleep_duration) { |
| is_suspend_imminent_ = false; |
| // |DismissUI| will restart the lock screen timer if lock screen is active and |
| // if Ambient mode is enabled, so call it when resuming from suspend to |
| // restart Ambient mode if applicable. |
| DismissUI(); |
| } |
| |
| void AmbientController::OnAuthScanDone( |
| const device::mojom::FingerprintMessagePtr msg, |
| const base::flat_map<std::string, std::vector<std::string>>& matches) { |
| DismissUI(); |
| } |
| |
| void AmbientController::OnUserActivity(const ui::Event* event) { |
| // The following events are handled separately so that we can consume them. |
| if (event->IsMouseEvent() || event->IsTouchEvent() || event->IsKeyEvent() || |
| event->IsFlingScrollEvent()) { |
| return; |
| } |
| // While |kPreview| is loading, don't |DismissUI| on user activity. |
| // Users can still |DismissUI| with mouse, touch, key or assistant events. |
| if (ambient_ui_model_.ui_visibility() == AmbientUiVisibility::kPreview && |
| !Shell::GetPrimaryRootWindowController()->HasAmbientWidget()) { |
| return; |
| } |
| DismissUI(); |
| } |
| |
| void AmbientController::OnKeyEvent(ui::KeyEvent* event) { |
| // Prevent dispatching key press event to the login UI. |
| event->StopPropagation(); |
| // |DismissUI| only on |ET_KEY_PRESSED|. Otherwise it won't be possible to |
| // start the preview by pressing "enter" key. It'll be cancelled immediately |
| // on |ET_KEY_RELEASED|. |
| if (event->type() == ui::ET_KEY_PRESSED) { |
| DismissUI(); |
| } |
| } |
| |
| void AmbientController::OnMouseEvent(ui::MouseEvent* event) { |
| // |DismissUI| on actual mouse move only if the screen saver widget is shown |
| // (images are downloaded). |
| if (event->type() == ui::ET_MOUSE_MOVED) { |
| MaybeDismissUIOnMouseMove(); |
| last_mouse_event_was_move_ = true; |
| return; |
| } |
| |
| // Prevent dispatching mouse event to the windows behind screen saver. |
| // Let move event pass through, so that it clears hover states. |
| event->StopPropagation(); |
| if (event->IsAnyButton()) { |
| DismissUI(); |
| } |
| last_mouse_event_was_move_ = false; |
| } |
| |
| void AmbientController::OnTouchEvent(ui::TouchEvent* event) { |
| // Prevent dispatching touch event to the windows behind screen saver. |
| event->StopPropagation(); |
| DismissUI(); |
| } |
| |
| void AmbientController::OnInteractionStateChanged( |
| InteractionState interaction_state) { |
| if (interaction_state == InteractionState::kActive) { |
| // Assistant is active. |
| DismissUI(); |
| } |
| } |
| |
| void AmbientController::ShowUi() { |
| DVLOG(1) << __func__; |
| |
| // TODO(meilinw): move the eligibility check to the idle entry point once |
| // implemented: b/149246117. |
| if (!IsAmbientModeEnabled()) { |
| LOG(WARNING) << "Ambient mode is not allowed."; |
| return; |
| } |
| |
| if (is_suspend_imminent_) { |
| VLOG(1) << "Do not show UI when suspend imminent"; |
| return; |
| } |
| |
| ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kShown); |
| } |
| |
| void AmbientController::StartScreenSaverPreview() { |
| if (!IsAmbientModeEnabled()) { |
| LOG(WARNING) << "Ambient mode is not allowed."; |
| return; |
| } |
| |
| ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kPreview); |
| base::RecordAction(base::UserMetricsAction(kScreenSaverPreviewUserAction)); |
| } |
| |
| void AmbientController::ShowHiddenUi() { |
| DVLOG(1) << __func__; |
| |
| if (!IsAmbientModeEnabled()) { |
| LOG(WARNING) << "Ambient mode is not allowed."; |
| return; |
| } |
| |
| if (is_suspend_imminent_) { |
| VLOG(1) << "Do not start hidden UI when suspend imminent"; |
| return; |
| } |
| |
| if (is_screen_off_) { |
| VLOG(1) << "Do not start hidden UI when screen is off"; |
| return; |
| } |
| |
| ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kHidden); |
| } |
| |
| void AmbientController::CloseUi(bool immediately) { |
| DVLOG(1) << __func__; |
| |
| close_widgets_immediately_ = immediately; |
| ambient_ui_model_.SetUiVisibility(AmbientUiVisibility::kClosed); |
| if (!Shell::Get()->IsInTabletMode()) { |
| Shell::Get()->cursor_manager()->ShowCursor(); |
| } |
| } |
| |
| void AmbientController::ToggleInSessionUi() { |
| if (ambient_ui_model_.ui_visibility() == AmbientUiVisibility::kClosed) |
| ShowUi(); |
| else |
| CloseUi(); |
| } |
| |
| bool AmbientController::IsShown() const { |
| return ambient_ui_model_.ui_visibility() == AmbientUiVisibility::kShown || |
| ambient_ui_model_.ui_visibility() == AmbientUiVisibility::kPreview; |
| } |
| |
| void AmbientController::AcquireWakeLock() { |
| if (!wake_lock_) { |
| mojo::Remote<device::mojom::WakeLockProvider> provider; |
| AmbientClient::Get()->RequestWakeLockProvider( |
| provider.BindNewPipeAndPassReceiver()); |
| provider->GetWakeLockWithoutContext( |
| device::mojom::WakeLockType::kPreventDisplaySleep, |
| device::mojom::WakeLockReason::kOther, kWakeLockReason, |
| wake_lock_.BindNewPipeAndPassReceiver()); |
| } |
| |
| DCHECK(wake_lock_); |
| wake_lock_->RequestWakeLock(); |
| VLOG(1) << "Acquired wake lock"; |
| |
| auto* session_controller = Shell::Get()->session_controller(); |
| if (session_controller->CanLockScreen() && |
| session_controller->ShouldLockScreenAutomatically()) { |
| if (!session_controller->IsScreenLocked() && |
| !delayed_lock_timer_.IsRunning()) { |
| delayed_lock_timer_.Start( |
| FROM_HERE, ambient_ui_model_.background_lock_screen_timeout(), |
| base::BindOnce( |
| []() { Shell::Get()->session_controller()->LockScreen(); })); |
| } |
| } |
| } |
| |
| void AmbientController::ReleaseWakeLock() { |
| if (!wake_lock_) |
| return; |
| |
| wake_lock_->CancelWakeLock(); |
| VLOG(1) << "Released wake lock"; |
| |
| delayed_lock_timer_.Stop(); |
| } |
| |
| void AmbientController::CloseAllWidgets(bool immediately) { |
| for (auto* root_window_controller : |
| RootWindowController::root_window_controllers()) { |
| root_window_controller->CloseAmbientWidget(immediately); |
| } |
| } |
| |
| void AmbientController::AddManagedScreensaverPolicyPrefObservers() { |
| pref_change_registrar_->Add( |
| ambient::prefs::kAmbientModeManagedScreensaverIdleTimeoutSeconds, |
| base::BindRepeating( |
| &AmbientController:: |
| OnManagedScreensaverLockScreenIdleTimeoutPrefChanged, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| pref_change_registrar_->Add( |
| ambient::prefs::kAmbientModeManagedScreensaverImageDisplayIntervalSeconds, |
| base::BindRepeating( |
| &AmbientController:: |
| OnManagedScreensaverPhotoRefreshIntervalPrefChanged, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| OnManagedScreensaverLockScreenIdleTimeoutPrefChanged(); |
| OnManagedScreensaverPhotoRefreshIntervalPrefChanged(); |
| } |
| |
| void AmbientController::RemoveAmbientModeSettingsPrefObservers() { |
| for (const auto* pref_name : |
| {ambient::prefs::kAmbientModeLockScreenBackgroundTimeoutSeconds, |
| ambient::prefs::kAmbientModeLockScreenInactivityTimeoutSeconds, |
| ambient::prefs::kAmbientModePhotoRefreshIntervalSeconds, |
| ambient::prefs::kAmbientUiSettings, |
| ambient::prefs::kAmbientModeAnimationPlaybackSpeed, |
| ambient::prefs::kAmbientModeManagedScreensaverIdleTimeoutSeconds, |
| ambient::prefs:: |
| kAmbientModeManagedScreensaverImageDisplayIntervalSeconds}) { |
| if (pref_change_registrar_->IsObserved(pref_name)) { |
| pref_change_registrar_->Remove(pref_name); |
| } |
| } |
| } |
| |
| void AmbientController::OnManagedScreensaverLockScreenIdleTimeoutPrefChanged() { |
| DCHECK(GetPrimaryUserPrefService()); |
| ambient_ui_model_.SetLockScreenInactivityTimeout( |
| base::Seconds(GetPrimaryUserPrefService()->GetInteger( |
| ambient::prefs::kAmbientModeManagedScreensaverIdleTimeoutSeconds))); |
| } |
| |
| void AmbientController::OnManagedScreensaverPhotoRefreshIntervalPrefChanged() { |
| DCHECK(GetPrimaryUserPrefService()); |
| ambient_ui_model_.SetPhotoRefreshInterval( |
| base::Seconds(GetPrimaryUserPrefService()->GetInteger( |
| ambient::prefs:: |
| kAmbientModeManagedScreensaverImageDisplayIntervalSeconds))); |
| } |
| |
| void AmbientController::OnManagedScreensaverEnabledPrefChanged() { |
| ResetAmbientControllerResources(); |
| OnEnabledPrefChanged(); |
| |
| if (IsAmbientModeManagedScreensaverEnabled()) { |
| RemoveAmbientModeSettingsPrefObservers(); |
| AddManagedScreensaverPolicyPrefObservers(); |
| return; |
| } |
| } |
| |
| void AmbientController::AddAmbientModeUserSettingsPolicyPrefObservers() { |
| pref_change_registrar_->Add( |
| ambient::prefs::kAmbientModeLockScreenInactivityTimeoutSeconds, |
| base::BindRepeating( |
| &AmbientController::OnLockScreenInactivityTimeoutPrefChanged, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| pref_change_registrar_->Add( |
| ambient::prefs::kAmbientModeLockScreenBackgroundTimeoutSeconds, |
| base::BindRepeating( |
| &AmbientController::OnLockScreenBackgroundTimeoutPrefChanged, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| pref_change_registrar_->Add( |
| ambient::prefs::kAmbientModePhotoRefreshIntervalSeconds, |
| base::BindRepeating(&AmbientController::OnPhotoRefreshIntervalPrefChanged, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| pref_change_registrar_->Add( |
| ambient::prefs::kAmbientUiSettings, |
| base::BindRepeating(&AmbientController::OnAmbientUiSettingsChanged, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| pref_change_registrar_->Add( |
| ambient::prefs::kAmbientModeAnimationPlaybackSpeed, |
| base::BindRepeating(&AmbientController::OnAnimationPlaybackSpeedChanged, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| // Trigger the callbacks manually the first time to init AmbientUiModel. |
| OnLockScreenInactivityTimeoutPrefChanged(); |
| OnLockScreenBackgroundTimeoutPrefChanged(); |
| OnPhotoRefreshIntervalPrefChanged(); |
| OnAnimationPlaybackSpeedChanged(); |
| } |
| |
| void AmbientController::OnEnabledPrefChanged() { |
| if (IsAmbientModeEnabled()) { |
| if (is_initialized_) { |
| LOG(WARNING) << "Ambient mode is already enabled"; |
| return; |
| } |
| DVLOG(1) << "Ambient mode enabled"; |
| // TODO(b/274165045): Remove this temporary way of circulating the video |
| // theme throughout the system once the hub supports the video theme. This |
| // is just for experimentation purposes until then. |
| SetUiSettingsForExperimentation(); |
| |
| AddAmbientModeUserSettingsPolicyPrefObservers(); |
| |
| photo_cache_ = AmbientPhotoCache::Create( |
| GetCacheRootPath().Append( |
| FILE_PATH_LITERAL(kAmbientModeCacheDirectoryName)), |
| *AmbientClient::Get(), access_token_controller_); |
| backup_photo_cache_ = AmbientPhotoCache::Create( |
| GetCacheRootPath().Append( |
| FILE_PATH_LITERAL(kAmbientModeBackupCacheDirectoryName)), |
| *AmbientClient::Get(), access_token_controller_); |
| CreateUiLauncher(); |
| |
| ambient_ui_model_observer_.Observe(&ambient_ui_model_); |
| auto* power_manager_client = chromeos::PowerManagerClient::Get(); |
| DCHECK(power_manager_client); |
| power_manager_client_observer_.Observe(power_manager_client); |
| |
| fingerprint_->AddFingerprintObserver( |
| fingerprint_observer_receiver_.BindNewPipeAndPassRemote()); |
| |
| ambient_animation_progress_tracker_ = |
| std::make_unique<AmbientAnimationProgressTracker>(); |
| |
| is_initialized_ = true; |
| } else { |
| DVLOG(1) << "Ambient mode disabled"; |
| ResetAmbientControllerResources(); |
| } |
| } |
| |
| void AmbientController::ResetAmbientControllerResources() { |
| CloseUi(); |
| |
| ambient_animation_progress_tracker_.reset(); |
| |
| RemoveAmbientModeSettingsPrefObservers(); |
| |
| ambient_ui_model_observer_.Reset(); |
| power_manager_client_observer_.Reset(); |
| |
| DestroyUiLauncher(); |
| backup_photo_cache_.reset(); |
| photo_cache_.reset(); |
| |
| if (fingerprint_observer_receiver_.is_bound()) { |
| fingerprint_observer_receiver_.reset(); |
| } |
| is_initialized_ = false; |
| } |
| |
| void AmbientController::OnLockScreenInactivityTimeoutPrefChanged() { |
| auto* pref_service = GetPrimaryUserPrefService(); |
| if (!pref_service) |
| return; |
| |
| ambient_ui_model_.SetLockScreenInactivityTimeout( |
| base::Seconds(pref_service->GetInteger( |
| ambient::prefs::kAmbientModeLockScreenInactivityTimeoutSeconds))); |
| } |
| |
| void AmbientController::OnLockScreenBackgroundTimeoutPrefChanged() { |
| auto* pref_service = GetPrimaryUserPrefService(); |
| if (!pref_service) |
| return; |
| |
| ambient_ui_model_.SetBackgroundLockScreenTimeout( |
| base::Seconds(pref_service->GetInteger( |
| ambient::prefs::kAmbientModeLockScreenBackgroundTimeoutSeconds))); |
| } |
| |
| void AmbientController::OnPhotoRefreshIntervalPrefChanged() { |
| auto* pref_service = GetPrimaryUserPrefService(); |
| if (!pref_service) |
| return; |
| |
| ambient_ui_model_.SetPhotoRefreshInterval( |
| base::Seconds(pref_service->GetInteger( |
| ambient::prefs::kAmbientModePhotoRefreshIntervalSeconds))); |
| } |
| |
| void AmbientController::OnAmbientUiSettingsChanged() { |
| DVLOG(4) << "AmbientUiSettings changed to " |
| << GetCurrentUiSettings().ToString(); |
| // For a given topic category, the topics downloaded from IMAX and saved to |
| // cache differ from theme to theme: |
| // 1) Slideshow mode keeps primary/related photos paired within a topic, |
| // whereas animated themes split the photos into 2 separate topics. |
| // 2) The resolution of the photos downloaded from FIFE may differ between |
| // themes, depending on the image assets' sizes in the animation file. |
| // For this reason, it is better to not re-use the cache when switching |
| // between themes. |
| // |
| // There are corner cases here where the theme may change and the program |
| // crashes before the cache gets cleared below. This is intentionally not |
| // accounted for because it's not worth the added complexity. If this |
| // should happen, re-using the cache will still work without fatal behavior. |
| // The UI may just not be optimal. Furthermore, the cache gradually gets |
| // overwritten with topics reflecting the new theme anyways, so ambient mode |
| // should not be stuck with a mismatched cache indefinitely. |
| CHECK(photo_cache_); |
| photo_cache_->Clear(); |
| |
| // The |AmbientUiLauncher| implementation to use is largely dependent on |
| // the current |AmbientUiSettings|, so this needs to be recreated. |
| CreateUiLauncher(); |
| } |
| |
| void AmbientController::OnAnimationPlaybackSpeedChanged() { |
| DCHECK(GetPrimaryUserPrefService()); |
| ambient_ui_model_.set_animation_playback_speed( |
| GetPrimaryUserPrefService()->GetDouble( |
| ambient::prefs::kAmbientModeAnimationPlaybackSpeed)); |
| } |
| |
| void AmbientController::RequestAccessToken( |
| AmbientAccessTokenController::AccessTokenCallback callback, |
| bool may_refresh_token_on_lock) { |
| // Do not request access tokens when the ambient mode is in the managed mode |
| // as we do not want to rely on any user information . |
| if (IsAmbientModeManagedScreensaverEnabled()) { |
| // Consume the callback to be resilient against dependencies on the callback |
| // in the future. |
| std::move(callback).Run("", ""); |
| return; |
| } |
| access_token_controller_.RequestAccessToken(std::move(callback), |
| may_refresh_token_on_lock); |
| } |
| |
| void AmbientController::DismissUI() { |
| if (!IsAmbientModeEnabled()) { |
| CloseUi(); |
| return; |
| } |
| |
| if (ambient_ui_model_.ui_visibility() == AmbientUiVisibility::kHidden) { |
| // Double resetting crashes the UI, make sure it is running. |
| if (inactivity_timer_.IsRunning()) { |
| inactivity_timer_.Reset(); |
| } |
| return; |
| } |
| |
| if (LockScreen::HasInstance()) { |
| ShowHiddenUi(); |
| return; |
| } |
| |
| CloseUi(); |
| } |
| |
| AmbientBackendModel* AmbientController::GetAmbientBackendModel() { |
| if (ambient_ui_launcher_) { |
| // This can legitimately be null. Some ambient UIs do not use photos at all |
| // and hence, do not have an active |AmbientBackendModel|. |
| // TODO(b/274164306): Move |AmbientBackendModel| references completely out |
| // of |AmbientController|. The business logic should be migrated elsewhere |
| // (likely somewhere within an |AmbientUiLauncher| implementation). |
| return ambient_ui_launcher_->GetAmbientBackendModel(); |
| } |
| |
| DCHECK(ambient_photo_controller_); |
| return ambient_photo_controller_->ambient_backend_model(); |
| } |
| |
| AmbientWeatherModel* AmbientController::GetAmbientWeatherModel() { |
| return ambient_weather_controller_->weather_model(); |
| } |
| |
| void AmbientController::OnImagesReady() { |
| CreateAndShowWidgets(); |
| } |
| |
| void AmbientController::OnImagesFailed() { |
| LOG(ERROR) << "Ambient mode failed to start"; |
| CloseUi(); |
| } |
| |
| std::unique_ptr<views::Widget> AmbientController::CreateWidget( |
| aura::Window* container) { |
| AmbientTheme current_theme = GetCurrentUiSettings().theme(); |
| std::unique_ptr<AmbientContainerView> container_view; |
| if (ambient_ui_launcher_) { |
| container_view = std::make_unique<AmbientContainerView>( |
| current_theme, ambient_ui_launcher_->CreateView(), |
| multi_screen_metrics_recorder_.get()); |
| } else { |
| // TODO(b/274164306): Everything should use |
| // |AmbientUiLauncher::CreateView()| when slideshow and animation themes |
| // are migrated to AmbientUiLauncher. |
| container_view = std::make_unique<AmbientContainerView>( |
| &delegate_, ambient_animation_progress_tracker_.get(), |
| AmbientAnimationStaticResources::Create(current_theme, |
| /*serializable=*/true), |
| multi_screen_metrics_recorder_.get(), frame_rate_controller_.get()); |
| } |
| auto* widget_delegate = new AmbientWidgetDelegate(); |
| widget_delegate->SetInitiallyFocusedView(container_view.get()); |
| |
| views::Widget::InitParams params; |
| params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; |
| params.name = GetWidgetName(); |
| params.show_state = ui::SHOW_STATE_FULLSCREEN; |
| params.parent = container; |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.delegate = widget_delegate; |
| params.visible_on_all_workspaces = true; |
| |
| // Do not change the video wake lock. |
| params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent; |
| |
| auto widget = std::make_unique<views::Widget>(); |
| widget->Init(std::move(params)); |
| auto* contents_view = widget->SetContentsView(std::move(container_view)); |
| |
| widget->SetVisibilityAnimationTransition( |
| views::Widget::VisibilityTransition::ANIMATE_BOTH); |
| ::wm::SetWindowVisibilityAnimationType( |
| widget->GetNativeWindow(), ::wm::WINDOW_VISIBILITY_ANIMATION_TYPE_FADE); |
| ::wm::SetWindowVisibilityChangesAnimated(widget->GetNativeWindow()); |
| |
| widget->Show(); |
| |
| if (ambient_ui_model_.ui_visibility() == AmbientUiVisibility::kShown) { |
| DCHECK(start_time_); |
| ambient::RecordAmbientModeStartupTime(base::Time::Now() - *start_time_, |
| current_theme); |
| } |
| |
| // Only announce for the primary window. |
| if (Shell::GetPrimaryRootWindow() == container->GetRootWindow()) { |
| contents_view->GetViewAccessibility().AnnounceText( |
| l10n_util::GetStringUTF16(IDS_ASH_SCREENSAVER_STARTS)); |
| } |
| |
| return widget; |
| } |
| |
| void AmbientController::CreateAndShowWidgets() { |
| if (ambient_ui_model_.ui_visibility() == AmbientUiVisibility::kPreview) { |
| preview_widget_created_at_ = base::Time::Now(); |
| } |
| // Hide cursor. |
| Shell::Get()->cursor_manager()->HideCursor(); |
| for (auto* root_window_controller : |
| RootWindowController::root_window_controllers()) { |
| root_window_controller->CreateAmbientWidget(); |
| } |
| } |
| |
| void AmbientController::StartRefreshingImages() { |
| DCHECK(ambient_photo_controller_); |
| // There is no use case for switching themes "on-the-fly" while ambient mode |
| // is rendering. Thus, it's sufficient to just reinitialize the |
| // model/controller with the appropriate config each time before calling |
| // StartScreenUpdate(). |
| DCHECK(!ambient_photo_controller_->IsScreenUpdateActive()); |
| AmbientTheme current_theme = GetCurrentUiSettings().theme(); |
| DVLOG(4) << "Loaded ambient theme " << ToString(current_theme); |
| |
| AmbientPhotoConfig photo_config; |
| std::unique_ptr<AmbientTopicQueue::Delegate> topic_queue_delegate; |
| if (current_theme == AmbientTheme::kSlideshow) { |
| photo_config = CreateAmbientSlideshowPhotoConfig(); |
| topic_queue_delegate = |
| std::make_unique<AmbientTopicQueueSlideshowDelegate>(); |
| } else { |
| scoped_refptr<cc::SkottieWrapper> animation = |
| AmbientAnimationStaticResources::Create(current_theme, |
| /*serializable=*/false) |
| ->GetSkottieWrapper(); |
| photo_config = |
| CreateAmbientAnimationPhotoConfig(animation->GetImageAssetMetadata()); |
| topic_queue_delegate = std::make_unique<AmbientTopicQueueAnimationDelegate>( |
| animation->GetImageAssetMetadata()); |
| } |
| ambient_photo_controller_->ambient_backend_model()->SetPhotoConfig( |
| std::move(photo_config)); |
| ambient_photo_controller_->StartScreenUpdate(std::move(topic_queue_delegate)); |
| } |
| |
| void AmbientController::StopScreensaver() { |
| CloseAllWidgets(close_widgets_immediately_); |
| if (ambient_ui_launcher_) { |
| ambient_ui_launcher_->Finalize(); |
| return; |
| } |
| DCHECK(ambient_photo_controller_); |
| ambient_photo_controller_->StopScreenUpdate(); |
| } |
| |
| void AmbientController::MaybeStartScreenSaver() { |
| // The screensaver may have already been started. |
| if (IsUiLauncherActive()) { |
| return; |
| } |
| |
| if (!user_activity_observer_.IsObserving()) |
| user_activity_observer_.Observe(ui::UserActivityDetector::Get()); |
| |
| // Add observer for assistant interaction model |
| AssistantInteractionController::Get()->GetModel()->AddObserver(this); |
| |
| multi_screen_metrics_recorder_ = |
| std::make_unique<AmbientMultiScreenMetricsRecorder>( |
| GetCurrentUiSettings().theme()); |
| frame_rate_controller_ = |
| std::make_unique<AmbientAnimationFrameRateController>( |
| Shell::Get()->frame_throttling_controller()); |
| |
| Shell::Get()->AddPreTargetHandler(this); |
| if (ambient_ui_launcher_) { |
| ambient_ui_launcher_->Initialize( |
| base::BindOnce(&AmbientController::CreateAndShowWidgets, |
| weak_ptr_factory_.GetWeakPtr())); |
| } else { |
| StartRefreshingImages(); |
| } |
| } |
| |
| AmbientUiSettings AmbientController::GetCurrentUiSettings() const { |
| CHECK(GetPrimaryUserPrefService()); |
| return AmbientUiSettings::ReadFromPrefService(*GetPrimaryUserPrefService()); |
| } |
| |
| void AmbientController::SetUiSettingsForExperimentation() { |
| if (features::IsTimeOfDayScreenSaverEnabled()) { |
| CHECK(GetPrimaryUserPrefService()); |
| AmbientUiSettings(AmbientTheme::kVideo, |
| features::kTimeOfDayScreenSaverVideo.Get()) |
| .WriteToPrefService(*GetPrimaryUserPrefService()); |
| } |
| } |
| |
| void AmbientController::MaybeDismissUIOnMouseMove() { |
| // If the move was not an actual mouse move event or the screen saver widget |
| // is not shown yet (images are not downloaded), don't dismiss. |
| if (!last_mouse_event_was_move_ || |
| !Shell::GetPrimaryRootWindowController()->HasAmbientWidget()) { |
| return; |
| } |
| |
| // In preview mode, don't dismiss until the timer stops running (avoids |
| // accidental dismissal). |
| if (ambient_ui_model_.ui_visibility() == AmbientUiVisibility::kPreview) { |
| auto elapsed = base::Time::Now() - preview_widget_created_at_; |
| if (elapsed < kDismissPreviewOnMouseMoveDelay) { |
| return; |
| } |
| } |
| DismissUI(); |
| } |
| |
| void AmbientController::CreateUiLauncher() { |
| if (IsUiLauncherActive()) { |
| // There are no known use cases where the AmbientUiSettings selected by the |
| // user can change while in the middle of an ambient session, but this is |
| // handled gracefully just in case. |
| LOG(DFATAL) << "Cannot reset the AmbientUiLauncher while it is active"; |
| return; |
| } |
| |
| DestroyUiLauncher(); |
| |
| if (IsAmbientModeManagedScreensaverEnabled()) { |
| ambient_ui_launcher_ = |
| std::make_unique<AmbientManagedSlideshowUiLauncher>(&delegate_); |
| } else if (GetCurrentUiSettings().theme() == AmbientTheme::kVideo) { |
| ambient_ui_launcher_ = |
| std::make_unique<AmbientVideoUiLauncher>(GetPrimaryUserPrefService()); |
| } else { |
| // TODO(b/274164306): Remove when slideshow and animation themes are |
| // migrated to AmbientUiLauncher. |
| CHECK(photo_cache_); |
| CHECK(backup_photo_cache_); |
| ambient_photo_controller_ = std::make_unique<AmbientPhotoController>( |
| *photo_cache_, *backup_photo_cache_, delegate_, |
| // The type of photo config specified here is actually irrelevant as |
| // it always gets reset with the correct configuration anyways in |
| // StartRefreshingImages() before ambient mode starts. |
| CreateAmbientSlideshowPhotoConfig()); |
| // The new UiLauncher API adds backend model observers in its |
| // implementation and thus the observer is not required when using the new |
| // codepath. |
| // TODO(esum) Get rid the ambient_backend_model_observer_ and |
| // corresponding methods once other photo controllers are migrated to the |
| // new API. |
| ambient_backend_model_observer_.Observe(GetAmbientBackendModel()); |
| } |
| } |
| |
| void AmbientController::DestroyUiLauncher() { |
| ambient_ui_launcher_.reset(); |
| // TODO(b/274164306): Remove when slideshow and animation themes are migrated |
| // to AmbientUiLauncher. |
| ambient_backend_model_observer_.Reset(); |
| ambient_photo_controller_.reset(); |
| } |
| |
| bool AmbientController::IsUiLauncherActive() const { |
| return (ambient_ui_launcher_ && ambient_ui_launcher_->IsActive()) || |
| // TODO(b/274164306): Remove when slideshow and animation themes are |
| // migrated to AmbientUiLauncher. |
| (ambient_photo_controller_ && |
| ambient_photo_controller_->IsScreenUpdateActive()); |
| } |
| |
| } // namespace ash |