chromium/ash/system/nearby_share/nearby_share_feature_pod_controller.cc

// Copyright 2020 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/system/nearby_share/nearby_share_feature_pod_controller.h"

#include "ash/constants/quick_settings_catalogs.h"
#include "ash/public/cpp/nearby_share_delegate.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/model/system_tray_model.h"
#include "ash/system/unified/feature_tile.h"
#include "ash/system/unified/quick_settings_metrics_util.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "base/functional/bind.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chromeos/constants/chromeos_features.h"
#include "ui/base/l10n/l10n_util.h"

namespace ash {

namespace {

constexpr base::TimeDelta kOneMinute = base::Minutes(1);
constexpr base::TimeDelta kOneSecond = base::Seconds(1);

std::u16string RemainingTimeString(base::TimeDelta remaining_time) {
  if (remaining_time > kOneMinute) {
    return l10n_util::GetStringFUTF16Int(
        IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_MINUTES,
        remaining_time.InMinutes() + 1);
  }

  return l10n_util::GetStringFUTF16Int(
      IDS_ASH_STATUS_TRAY_NEARBY_SHARE_REMAINING_SECONDS,
      static_cast<int>(remaining_time.InSeconds()) + 1);
}

}  // namespace

NearbyShareFeaturePodController::NearbyShareFeaturePodController(
    UnifiedSystemTrayController* tray_controller)
    : countdown_timer_(
          FROM_HERE,
          kOneSecond,
          base::BindRepeating(&NearbyShareFeaturePodController::UpdateButton,
                              base::Unretained(this),
                              /*enabled=*/true)),
      tray_controller_(tray_controller),
      nearby_share_delegate_(Shell::Get()->nearby_share_delegate()),
      nearby_share_controller_(Shell::Get()->nearby_share_controller()) {
  DCHECK(tray_controller_);
  DCHECK(nearby_share_delegate_);
  DCHECK(nearby_share_controller_);
  nearby_share_controller_->AddObserver(this);
}

NearbyShareFeaturePodController::~NearbyShareFeaturePodController() {
  nearby_share_controller_->RemoveObserver(this);
}

std::unique_ptr<FeatureTile> NearbyShareFeaturePodController::CreateTile(
    bool compact) {
  std::unique_ptr<FeatureTile> tile;
  if (chromeos::features::IsQuickShareV2Enabled()) {
    tile = std::make_unique<FeatureTile>(
        base::BindRepeating(&FeaturePodControllerBase::OnLabelPressed,
                            weak_ptr_factory_.GetWeakPtr()));
  } else {
    tile = std::make_unique<FeatureTile>(
        base::BindRepeating(&FeaturePodControllerBase::OnIconPressed,
                            weak_ptr_factory_.GetWeakPtr()));
  }

  tile_ = tile.get();

  SessionControllerImpl* session_controller =
      Shell::Get()->session_controller();

  const bool target_visibility =
      nearby_share_delegate_->IsPodButtonVisible() &&
      session_controller->IsActiveUserSessionStarted() &&
      session_controller->IsUserPrimary() &&
      !session_controller->IsUserSessionBlocked() &&
      nearby_share_delegate_->IsEnabled();
  tile_->SetVisible(target_visibility);
  if (target_visibility) {
    TrackVisibilityUMA();
  }
  const std::u16string feature_name =
      nearby_share_delegate_->GetPlaceholderFeatureName();
  tile_->SetLabel(
      feature_name.empty()
          ? l10n_util::GetStringUTF16(
                IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TILE_LABEL)
          : l10n_util::GetStringFUTF16(
                IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TILE_LABEL_PH, feature_name));

  if (chromeos::features::IsQuickShareV2Enabled()) {
    // TODO(brandosocarras, b/355325622):Indicate Quick Share visibility in
    // sublabel.
    tile_->SetSubLabel(
        l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TILE_LABEL));
    tile_->CreateDecorativeDrillInArrow();
    tile_->SetIconClickable(true);
    tile_->SetIconClickCallback(
        base::BindRepeating(&NearbyShareFeaturePodController::OnIconPressed,
                            weak_ptr_factory_.GetWeakPtr()));

    // Set tile appearance.
    UpdateQSv2Button();
  } else {
    tile_->SetTooltipText(
        feature_name.empty()
            ? l10n_util::GetStringUTF16(
                  IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TOGGLE_TOOLTIP)
            : l10n_util::GetStringFUTF16(
                  IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TOGGLE_TOOLTIP_PH,
                  feature_name));
    bool enabled = nearby_share_delegate_->IsHighVisibilityOn();
    OnHighVisibilityEnabledChanged(enabled);
  }
  return tile;
}

QsFeatureCatalogName NearbyShareFeaturePodController::GetCatalogName() {
  return QsFeatureCatalogName::kNearbyShare;
}

void NearbyShareFeaturePodController::OnIconPressed() {
  if (chromeos::features::IsQuickShareV2Enabled()) {
    // TODO(brandosocarras, b/358691432): Toggle Quick Share on icon press.
    return;
  }

  TrackToggleUMA(
      /*target_toggle_state=*/!nearby_share_delegate_->IsHighVisibilityOn());
  if (nearby_share_delegate_->IsHighVisibilityOn()) {
    nearby_share_delegate_->DisableHighVisibility();
  } else {
    nearby_share_delegate_->EnableHighVisibility();
  }
}

void NearbyShareFeaturePodController::OnLabelPressed() {
  if (chromeos::features::IsQuickShareV2Enabled()) {
    tray_controller_->ShowNearbyShareDetailedView();
    return;
  }

  TrackDiveInUMA();
  nearby_share_delegate_->ShowNearbyShareSettings();
}

void NearbyShareFeaturePodController::OnHighVisibilityEnabledChanged(
    bool enabled) {
  if (enabled) {
    shutoff_time_ = nearby_share_delegate_->HighVisibilityShutoffTime();
    countdown_timer_.Reset();
  } else {
    countdown_timer_.Stop();
  }
  UpdateButton(enabled);
}

void NearbyShareFeaturePodController::OnVisibilityChanged(
    ::nearby_share::mojom::Visibility visibility) {
  UpdateQSv2Button();
}

void NearbyShareFeaturePodController::UpdateButton(bool enabled) {
  tile_->SetToggled(enabled);

  auto& icon = nearby_share_delegate_->GetIcon(/*on_icon=*/enabled);
  if (enabled) {
    tile_->SetVectorIcon(icon.is_empty() ? kQuickSettingsNearbyShareOnIcon
                                         : icon);
    tile_->SetSubLabel(l10n_util::GetStringFUTF16(
        IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TILE_ON_STATE,
        RemainingTimeString(RemainingHighVisibilityTime())));

  } else {
    tile_->SetVectorIcon(icon.is_empty() ? kQuickSettingsNearbyShareOffIcon
                                         : icon);
    tile_->SetSubLabel(l10n_util::GetStringUTF16(
        IDS_ASH_STATUS_TRAY_NEARBY_SHARE_TILE_OFF_STATE));
  }
}

void NearbyShareFeaturePodController::UpdateQSv2Button() {
  if (!chromeos::features::IsQuickShareV2Enabled()) {
    return;
  }

  bool in_high_visibility = nearby_share_delegate_->IsHighVisibilityOn();

  if (in_high_visibility) {
    ToggleTileOn();
    return;
  }

  ::nearby_share::mojom::Visibility visibility =
      nearby_share_delegate_->GetVisibility();

  switch (visibility) {
    case ::nearby_share::mojom::Visibility::kAllContacts:
      [[fallthrough]];
    case ::nearby_share::mojom::Visibility::kYourDevices:
      [[fallthrough]];
    case ::nearby_share::mojom::Visibility::kSelectedContacts:
      ToggleTileOn();
      break;
    case ::nearby_share::mojom::Visibility::kUnknown:
      [[fallthrough]];
    case ::nearby_share::mojom::Visibility::kNoOne:
      ToggleTileOff();
  }
}

void NearbyShareFeaturePodController::ToggleTileOn() {
  auto& on_icon = nearby_share_delegate_->GetIcon(/*on_icon=*/true);
  tile_->SetVectorIcon(on_icon.is_empty() ? kQuickSettingsNearbyShareOnIcon
                                          : on_icon);
  tile_->SetToggled(true);
}

void NearbyShareFeaturePodController::ToggleTileOff() {
  auto& off_icon = nearby_share_delegate_->GetIcon(/*on_icon=*/false);
  tile_->SetVectorIcon(off_icon.is_empty() ? kQuickSettingsNearbyShareOffIcon
                                           : off_icon);
  tile_->SetToggled(false);
}

base::TimeDelta NearbyShareFeaturePodController::RemainingHighVisibilityTime()
    const {
  base::TimeTicks now = base::TimeTicks::Now();
  return shutoff_time_ > now ? shutoff_time_ - now : base::Seconds(0);
}

}  // namespace ash