blob: 589e034bddccdcb5ada4b2998abe4826c3b021a2 [file] [log] [blame]
// Copyright 2022 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/hid/hid_status_icon.h"
#include <string>
#include <vector>
#include "base/containers/contains.h"
#include "base/containers/cxx20_erase_vector.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/hid/hid_connection_tracker.h"
#include "chrome/browser/hid/hid_connection_tracker_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/status_icons/status_icon.h"
#include "chrome/browser/status_icons/status_icon_menu_model.h"
#include "chrome/browser/status_icons/status_tray.h"
#include "extensions/buildflags/buildflags.h"
HidStatusIcon::HidStatusIcon() = default;
HidStatusIcon::~HidStatusIcon() {
if (status_icon_) {
auto* status_tray = g_browser_process->status_tray();
DCHECK(status_tray);
status_tray->RemoveStatusIcon(status_icon_);
}
}
void HidStatusIcon::AddProfile(Profile* profile) {
DCHECK(!base::Contains(profiles_, profile));
profiles_.emplace_back(profile);
RefreshIcon();
}
void HidStatusIcon::RemoveProfile(Profile* profile) {
DCHECK(base::Contains(profiles_, profile));
base::EraseIf(profiles_, [&](Profile* entry) { return profile == entry; });
RefreshIcon();
}
void HidStatusIcon::NotifyConnectionCountUpdated(Profile* profile) {
DCHECK(status_icon_);
DCHECK(base::Contains(profiles_, profile));
status_icon_->SetToolTip(GetTooltipLabel(GetTotalConnectionCount()));
}
void HidStatusIcon::ExecuteCommand(int command_id, int event_flags) {
DCHECK_GE(command_id, IDC_MANAGE_HID_DEVICES_FIRST);
DCHECK_LE(command_id, IDC_MANAGE_HID_DEVICES_LAST);
size_t profile_idx = command_id - IDC_MANAGE_HID_DEVICES_FIRST;
if (profile_idx < profiles_.size()) {
// |profiles_[profile_idx]|'s HidConnectionTracker guarantees the entry in
// |profiles_| is removed when the profile is destroyed.
auto* hid_connection_tracker = HidConnectionTrackerFactory::GetForProfile(
profiles_[profile_idx], /*create=*/false);
DCHECK(hid_connection_tracker);
hid_connection_tracker->ShowHidContentSettingsExceptions();
}
}
size_t HidStatusIcon::GetTotalConnectionCount() {
size_t total_connection_count = 0;
for (Profile* profile : profiles_) {
auto* hid_connection_tracker =
HidConnectionTrackerFactory::GetForProfile(profile, /*create=*/false);
DCHECK(hid_connection_tracker);
total_connection_count += hid_connection_tracker->connection_count();
}
return total_connection_count;
}
void HidStatusIcon::RefreshIcon() {
auto* status_tray = g_browser_process->status_tray();
DCHECK(status_tray);
if (profiles_.empty()) {
DCHECK(status_icon_);
status_tray->RemoveStatusIcon(status_icon_);
status_icon_ = nullptr;
return;
}
auto menu = std::make_unique<StatusIconMenuModel>(this);
for (size_t idx = 0; idx < profiles_.size(); idx++) {
int command_id = IDC_MANAGE_HID_DEVICES_FIRST + static_cast<int>(idx);
if (command_id > IDC_MANAGE_HID_DEVICES_LAST) {
// This case should be fairly rare, but if we have more profiles than
// pre-defined command ids, we don't put those in the status icon menu.
// TODO(crbug.com/1360981): Add a metric to capture this.
break;
}
menu->AddItem(command_id, GetManageHidDeviceButtonLabel(profiles_[idx]));
}
auto tooltip_label = GetTooltipLabel(GetTotalConnectionCount());
if (!status_icon_) {
status_icon_ = status_tray->CreateStatusIcon(
StatusTray::OTHER_ICON, GetStatusTrayIcon(), tooltip_label);
} else {
status_icon_->SetToolTip(tooltip_label);
}
status_icon_->SetContextMenu(std::move(menu));
}