chromium/chrome/browser/ui/ash/birch/birch_self_share_provider.cc

// Copyright 2024 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/ui/ash/birch/birch_self_share_provider.h"

#include "ash/birch/birch_item.h"
#include "ash/birch/birch_model.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/shell.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/send_tab_to_self_sync_service_factory.h"
#include "components/prefs/pref_service.h"
#include "components/send_tab_to_self/send_tab_to_self_entry.h"
#include "components/send_tab_to_self/send_tab_to_self_model.h"
#include "components/send_tab_to_self/target_device_info.h"

namespace ash {

namespace {

// The time before a `SendTabToSelfEntry` should be excluded from the
// `BirchModel`. This is the same expiration time for a device in
// `GetTargetDeviceInfoSortedList` for `SendTabToSelfBridge`.
constexpr base::TimeDelta kEntryExpiration = base::Days(10);

bool IsEntryExpired(base::Time shared_time) {
  return base::Time::Now() - shared_time > kEntryExpiration;
}

}  // namespace

BirchSelfShareProvider::BirchSelfShareProvider(Profile* profile)
    : profile_(profile),
      sync_service_(SendTabToSelfSyncServiceFactory::GetForProfile(profile)) {}

BirchSelfShareProvider::~BirchSelfShareProvider() = default;

void BirchSelfShareProvider::RequestBirchDataFetch() {
  const auto* const pref_service = profile_->GetPrefs();
  if (!pref_service ||
      !base::Contains(pref_service->GetList(
                          prefs::kContextualGoogleIntegrationsConfiguration),
                      prefs::kChromeSyncIntegrationName)) {
    // ChromeSync integration is disabled by policy.
    Shell::Get()->birch_model()->SetSelfShareItems({});
    return;
  }

  bool refresh = false;

  send_tab_to_self::SendTabToSelfModel* model =
      sync_service_->GetSendTabToSelfModel();

  std::set<std::string> cached_guids;
  for (const auto& item : items_) {
    cached_guids.insert(base::UTF16ToUTF8(item.guid()));
  }

  const std::vector<std::string> new_guids = model->GetAllGuids();

  // If there are any differences between cached guids and new guids, our
  // current list is dirty and we need to refresh.
  if (cached_guids.size() != new_guids.size() ||
      !std::equal(cached_guids.begin(), cached_guids.end(),
                  new_guids.begin())) {
    refresh = true;
  } else {
    // If there are no differences in guids, we check if any entries are opened.
    // Since we cannot determine if any cached entries are opened, we
    // have to iterate through the model entries.
    for (const std::string& guid : new_guids) {
      const send_tab_to_self::SendTabToSelfEntry* entry =
          model->GetEntryByGUID(guid);
      if (entry && entry->IsOpened()) {
        refresh = true;
      }
    }
  }

  if (!refresh) {
    Shell::Get()->birch_model()->SetSelfShareItems(std::move(items_));
    return;
  }

  items_.clear();

  for (std::string guid : new_guids) {
    const send_tab_to_self::SendTabToSelfEntry* entry =
        model->GetEntryByGUID(guid);
    if (entry && !entry->IsOpened() &&
        !IsEntryExpired(entry->GetSharedTime())) {
      const std::string entry_guid = entry->GetGUID();
      const std::string device_cache_guid =
          entry->GetTargetDeviceSyncCacheGuid();
      std::vector<send_tab_to_self::TargetDeviceInfo> device_info_list =
          model->GetTargetDeviceInfoSortedList();
      // Find the origin device that the entry was shared from using its
      // `target_device_sync_cache_guid_`.
      auto it = std::find_if(
          device_info_list.begin(), device_info_list.end(),
          [&device_cache_guid](
              const send_tab_to_self::TargetDeviceInfo& device_info) {
            return device_info.cache_guid == device_cache_guid;
          });

      // We set the `secondary_icon_type` of the birch item based on the origin
      // device's form factor.
      SecondaryIconType secondary_icon_type = SecondaryIconType::kNoIcon;
      if (it != device_info_list.end()) {
        send_tab_to_self::TargetDeviceInfo* matched_device_info = &(*it);
        switch (matched_device_info->form_factor) {
          case syncer::DeviceInfo::FormFactor::kDesktop:
            secondary_icon_type = SecondaryIconType::kTabFromDesktop;
            break;
          case syncer::DeviceInfo::FormFactor::kPhone:
            secondary_icon_type = SecondaryIconType::kTabFromPhone;
            break;
          case syncer::DeviceInfo::FormFactor::kTablet:
            secondary_icon_type = SecondaryIconType::kTabFromTablet;
            break;
          default:
            secondary_icon_type = SecondaryIconType::kNoIcon;
        }
      }
      items_.emplace_back(
          base::UTF8ToUTF16(entry_guid), base::UTF8ToUTF16(entry->GetTitle()),
          entry->GetURL(), entry->GetSharedTime(),
          base::UTF8ToUTF16(entry->GetDeviceName()), secondary_icon_type,
          base::BindRepeating(&BirchSelfShareProvider::OnItemPressed,
                              weak_factory_.GetWeakPtr(), entry_guid));
    }
  }
  Shell::Get()->birch_model()->SetSelfShareItems(std::move(items_));
}

void BirchSelfShareProvider::OnItemPressed(const std::string& guid) {
  send_tab_to_self::SendTabToSelfModel* model =
      sync_service_->GetSendTabToSelfModel();
  model->MarkEntryOpened(guid);
}

}  // namespace ash