blob: bebe5c1202208ca0e422fde999cf9e080db13b2b [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/download/bubble/download_display_controller.h"
#include "base/functional/bind.h"
#include "base/numerics/safe_conversions.h"
#include "base/power_monitor/power_monitor.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/download/bubble/download_bubble_prefs.h"
#include "chrome/browser/download/bubble/download_bubble_ui_controller.h"
#include "chrome/browser/download/bubble/download_bubble_utils.h"
#include "chrome/browser/download/bubble/download_display.h"
#include "chrome/browser/download/bubble/download_icon_state.h"
#include "chrome/browser/download/download_core_service.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/download/download_item_model.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/download/download_ui_controller.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/download/download_item_mode.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_bubble_type.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/offline_items_collection/core/offline_item.h"
#include "components/offline_items_collection/core/offline_item_state.h"
namespace {
using DownloadIconState = download::DownloadIconState;
using DownloadUIModelPtr = DownloadUIModel::DownloadUIModelPtr;
// The amount of time for the toolbar icon to be visible after a download is
// completed.
constexpr base::TimeDelta kToolbarIconVisibilityTimeInterval =
base::Minutes(60);
// The amount of time for the toolbar icon to stay active after a download is
// completed. If the download completed while full screen, the timer is started
// after user comes out of the full screen.
constexpr base::TimeDelta kToolbarIconActiveTimeInterval = base::Minutes(1);
// Whether there are no more in-progress downloads that are not paused, i.e.,
// whether all actively downloading items are done.
bool IsAllDone(const DownloadDisplayController::AllDownloadUIModelsInfo& info) {
return info.in_progress_count == info.paused_count;
}
} // namespace
DownloadDisplayController::DownloadDisplayController(
DownloadDisplay* display,
Browser* browser,
DownloadBubbleUIController* bubble_controller)
: display_(display),
browser_(browser),
bubble_controller_(bubble_controller) {
bubble_controller_->SetDownloadDisplayController(this);
// |display| can be null in tests.
if (display) {
MaybeShowButtonWhenCreated();
}
base::PowerMonitor::AddPowerSuspendObserver(this);
}
DownloadDisplayController::~DownloadDisplayController() {
base::PowerMonitor::RemovePowerSuspendObserver(this);
}
void DownloadDisplayController::OnNewItem(bool show_animation) {
if (!download::ShouldShowDownloadBubble(browser_->profile())) {
return;
}
UpdateButtonStateFromAllModelsInfo();
if (display_->IsFullscreenWithParentViewHidden()) {
fullscreen_notification_shown_ = true;
ExclusiveAccessContext* exclusive_access_context =
browser_->exclusive_access_manager()->context();
// exclusive_access_context can be null in tests.
if (exclusive_access_context) {
exclusive_access_context->UpdateExclusiveAccessExitBubbleContent(
GURL(), EXCLUSIVE_ACCESS_BUBBLE_TYPE_NONE,
ExclusiveAccessBubbleHideCallback(),
/*notify_download=*/true,
/*force_update=*/true);
}
} else {
display_->UpdateDownloadIcon(show_animation);
}
}
void DownloadDisplayController::OnUpdatedItem(bool is_done,
bool may_show_details) {
if (!download::ShouldShowDownloadBubble(browser_->profile())) {
return;
}
const AllDownloadUIModelsInfo& info = UpdateButtonStateFromAllModelsInfo();
bool will_show_details = may_show_details && is_done && IsAllDone(info);
if (is_done) {
ScheduleToolbarDisappearance(kToolbarIconVisibilityTimeInterval);
}
if (will_show_details && display_->IsFullscreenWithParentViewHidden()) {
// Suppress the complete event for now because the parent view is
// hidden.
details_shown_while_fullscreen_ = true;
will_show_details = false;
}
if (will_show_details) {
display_->ShowDetails();
}
}
void DownloadDisplayController::OnRemovedItem(const ContentId& id) {
if (!download::ShouldShowDownloadBubble(browser_->profile())) {
return;
}
UpdateButtonStateFromAllModelsInfo();
}
void DownloadDisplayController::OnButtonPressed() {
DownloadUIController* download_ui_controller =
DownloadCoreServiceFactory::GetForBrowserContext(
browser_->profile()->GetOriginalProfile())
->GetDownloadUIController();
if (download_ui_controller) {
download_ui_controller->OnButtonClicked();
}
}
void DownloadDisplayController::HandleButtonPressed() {
// If the current state is kComplete, set the icon to inactive because of the
// user action.
if (icon_info_.icon_state == DownloadIconState::kComplete) {
icon_info_.is_active = false;
}
display_->UpdateDownloadIcon(/*show_animation=*/false);
}
void DownloadDisplayController::ShowToolbarButton() {
if (!display_->IsShowing()) {
display_->Enable();
display_->Show();
}
}
void DownloadDisplayController::HideToolbarButton() {
if (display_->IsShowing()) {
display_->Hide();
}
}
void DownloadDisplayController::HideBubble() {
if (display_->IsShowingDetails()) {
display_->HideDetails();
}
}
void DownloadDisplayController::ListenToFullScreenChanges() {
observation_.Observe(
browser_->exclusive_access_manager()->fullscreen_controller());
}
void DownloadDisplayController::OnFullscreenStateChanged() {
if ((!fullscreen_notification_shown_ && !details_shown_while_fullscreen_) ||
display_->IsFullscreenWithParentViewHidden()) {
return;
}
fullscreen_notification_shown_ = false;
UpdateButtonStateFromAllModelsInfo();
if (download::ShouldShowDownloadBubble(browser_->profile()) &&
details_shown_while_fullscreen_) {
display_->ShowDetails();
details_shown_while_fullscreen_ = false;
}
}
void DownloadDisplayController::OnResume() {
UpdateButtonStateFromAllModelsInfo();
}
void DownloadDisplayController::UpdateToolbarButtonState(
const DownloadDisplayController::AllDownloadUIModelsInfo& info) {
if (info.all_models_size == 0) {
HideToolbarButton();
return;
}
base::Time last_complete_time = GetLastCompleteTime(info.last_completed_time);
if (info.in_progress_count > 0) {
icon_info_.icon_state = DownloadIconState::kProgress;
icon_info_.is_active = info.paused_count < info.in_progress_count;
} else {
icon_info_.icon_state = DownloadIconState::kComplete;
bool complete_unactioned =
HasRecentCompleteDownload(kToolbarIconActiveTimeInterval,
last_complete_time) &&
info.has_unactioned;
bool exited_fullscreen_owed_details =
!display_->IsFullscreenWithParentViewHidden() &&
details_shown_while_fullscreen_;
if (complete_unactioned || exited_fullscreen_owed_details) {
icon_info_.is_active = true;
ScheduleToolbarInactive(kToolbarIconActiveTimeInterval);
} else {
icon_info_.is_active = false;
}
}
if (info.has_deep_scanning) {
icon_info_.icon_state = DownloadIconState::kDeepScanning;
}
if (icon_info_.icon_state != DownloadIconState::kComplete ||
HasRecentCompleteDownload(kToolbarIconVisibilityTimeInterval,
last_complete_time)) {
ShowToolbarButton();
}
display_->UpdateDownloadIcon(/*show_animation=*/false);
}
void DownloadDisplayController::UpdateDownloadIconToInactive() {
icon_info_.is_active = false;
display_->UpdateDownloadIcon(/*show_animation=*/false);
}
const DownloadDisplayController::AllDownloadUIModelsInfo&
DownloadDisplayController::UpdateButtonStateFromAllModelsInfo() {
const AllDownloadUIModelsInfo& info =
bubble_controller_->update_service()->GetAllModelsInfo();
UpdateToolbarButtonState(info);
return info;
}
void DownloadDisplayController::ScheduleToolbarDisappearance(
base::TimeDelta interval) {
icon_disappearance_timer_.Stop();
icon_disappearance_timer_.Start(
FROM_HERE, interval, this, &DownloadDisplayController::HideToolbarButton);
}
void DownloadDisplayController::ScheduleToolbarInactive(
base::TimeDelta interval) {
icon_inactive_timer_.Stop();
icon_inactive_timer_.Start(
FROM_HERE, interval, this,
&DownloadDisplayController::UpdateDownloadIconToInactive);
}
base::Time DownloadDisplayController::GetLastCompleteTime(
base::Time last_completed_time_from_current_models) const {
base::Time last_time = DownloadPrefs::FromBrowserContext(browser_->profile())
->GetLastCompleteTime();
return std::max(last_time, last_completed_time_from_current_models);
}
void DownloadDisplayController::MaybeShowButtonWhenCreated() {
if (!download::ShouldShowDownloadBubble(browser_->profile())) {
return;
}
const AllDownloadUIModelsInfo& info = UpdateButtonStateFromAllModelsInfo();
if (display_->IsShowing()) {
ScheduleToolbarDisappearance(
kToolbarIconVisibilityTimeInterval -
(base::Time::Now() - GetLastCompleteTime(info.last_completed_time)));
}
}
bool DownloadDisplayController::HasRecentCompleteDownload(
base::TimeDelta interval,
base::Time last_complete_time) {
base::Time current_time = base::Time::Now();
base::TimeDelta time_since_last_completion =
current_time - last_complete_time;
// Also check that the current time is not smaller than the last complete
// time, this can happen if the system clock has moved backward.
return time_since_last_completion < interval &&
current_time >= last_complete_time;
}
DownloadDisplayController::IconInfo DownloadDisplayController::GetIconInfo() {
return icon_info_;
}
bool DownloadDisplayController::IsDisplayShowingDetails() {
return display_->IsShowingDetails();
}
DownloadDisplayController::ProgressInfo
DownloadDisplayController::GetProgress() {
return bubble_controller_->update_service()->GetProgressInfo();
}