1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.h"
6 
7 #include "base/bind.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "build/build_config.h"
10 #include "chrome/app/chrome_command_ids.h"
11 #include "chrome/browser/renderer_context_menu/render_view_context_menu.h"
12 #include "chrome/browser/sharing/click_to_call/click_to_call_metrics.h"
13 #include "chrome/browser/sharing/click_to_call/click_to_call_ui_controller.h"
14 #include "chrome/browser/sharing/click_to_call/feature.h"
15 #include "chrome/browser/sharing/sharing_constants.h"
16 #include "chrome/grit/generated_resources.h"
17 #include "components/sync_device_info/device_info.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/base/models/image_model.h"
20 #include "ui/gfx/color_palette.h"
21 #include "ui/gfx/paint_vector_icon.h"
22 
SubMenuDelegate(ClickToCallContextMenuObserver * parent)23 ClickToCallContextMenuObserver::SubMenuDelegate::SubMenuDelegate(
24     ClickToCallContextMenuObserver* parent)
25     : parent_(parent) {}
26 
27 ClickToCallContextMenuObserver::SubMenuDelegate::~SubMenuDelegate() = default;
28 
IsCommandIdEnabled(int command_id) const29 bool ClickToCallContextMenuObserver::SubMenuDelegate::IsCommandIdEnabled(
30     int command_id) const {
31   // All supported commands are enabled in sub menu.
32   return true;
33 }
34 
ExecuteCommand(int command_id,int event_flags)35 void ClickToCallContextMenuObserver::SubMenuDelegate::ExecuteCommand(
36     int command_id,
37     int event_flags) {
38   if (command_id < kSubMenuFirstDeviceCommandId ||
39       command_id > kSubMenuLastDeviceCommandId)
40     return;
41   int device_index = command_id - kSubMenuFirstDeviceCommandId;
42   parent_->SendClickToCallMessage(device_index);
43 }
44 
ClickToCallContextMenuObserver(RenderViewContextMenuProxy * proxy)45 ClickToCallContextMenuObserver::ClickToCallContextMenuObserver(
46     RenderViewContextMenuProxy* proxy)
47     : proxy_(proxy),
48       controller_(ClickToCallUiController::GetOrCreateFromWebContents(
49           proxy_->GetWebContents())) {}
50 
51 ClickToCallContextMenuObserver::~ClickToCallContextMenuObserver() = default;
52 
BuildMenu(const std::string & phone_number,const std::string & selection_text,SharingClickToCallEntryPoint entry_point)53 void ClickToCallContextMenuObserver::BuildMenu(
54     const std::string& phone_number,
55     const std::string& selection_text,
56     SharingClickToCallEntryPoint entry_point) {
57   DCHECK(!phone_number.empty());
58 
59   phone_number_ = phone_number;
60   selection_text_ = selection_text;
61   entry_point_ = entry_point;
62   devices_ = controller_->GetDevices();
63   LogSharingDevicesToShow(controller_->GetFeatureMetricsPrefix(),
64                           kSharingUiContextMenu, devices_.size());
65   if (devices_.empty())
66     return;
67 
68   if (devices_.size() == 1) {
69 #if defined(OS_MAC)
70     proxy_->AddMenuItem(
71         IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE,
72         l10n_util::GetStringFUTF16(
73             IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE,
74             base::UTF8ToUTF16(devices_[0]->client_name())));
75 #else
76     proxy_->AddMenuItemWithIcon(
77         IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE,
78         l10n_util::GetStringFUTF16(
79             IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE,
80             base::UTF8ToUTF16(devices_[0]->client_name())),
81         ui::ImageModel::FromVectorIcon(controller_->GetVectorIcon(),
82                                        /*color_id=*/-1,
83                                        ui::SimpleMenuModel::kDefaultIconSize));
84 #endif
85   } else {
86     BuildSubMenu();
87 #if defined(OS_MAC)
88     proxy_->AddSubMenu(
89         IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES,
90         l10n_util::GetStringUTF16(
91             IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES),
92         sub_menu_model_.get());
93 #else
94     proxy_->AddSubMenuWithStringIdAndIcon(
95         IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES,
96         IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES,
97         sub_menu_model_.get(),
98         ui::ImageModel::FromVectorIcon(controller_->GetVectorIcon(),
99                                        /*color_id=*/-1,
100                                        ui::SimpleMenuModel::kDefaultIconSize));
101 #endif
102   }
103 }
104 
BuildSubMenu()105 void ClickToCallContextMenuObserver::BuildSubMenu() {
106   sub_menu_model_ = std::make_unique<ui::SimpleMenuModel>(&sub_menu_delegate_);
107 
108   int command_id = kSubMenuFirstDeviceCommandId;
109   for (const auto& device : devices_) {
110     if (command_id > kSubMenuLastDeviceCommandId)
111       break;
112     sub_menu_model_->AddItem(command_id++,
113                              base::UTF8ToUTF16(device->client_name()));
114   }
115 }
116 
IsCommandIdSupported(int command_id)117 bool ClickToCallContextMenuObserver::IsCommandIdSupported(int command_id) {
118   size_t device_count = devices_.size();
119   if (device_count == 0)
120     return false;
121 
122   if (device_count == 1) {
123     return command_id ==
124            IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE;
125   } else {
126     return command_id ==
127            IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES;
128   }
129 }
130 
IsCommandIdEnabled(int command_id)131 bool ClickToCallContextMenuObserver::IsCommandIdEnabled(int command_id) {
132   // All supported commands are enabled.
133   return true;
134 }
135 
ExecuteCommand(int command_id)136 void ClickToCallContextMenuObserver::ExecuteCommand(int command_id) {
137   if (command_id == IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE) {
138     DCHECK_EQ(1u, devices_.size());
139     SendClickToCallMessage(0);
140   }
141 }
142 
SendClickToCallMessage(int chosen_device_index)143 void ClickToCallContextMenuObserver::SendClickToCallMessage(
144     int chosen_device_index) {
145   DCHECK(entry_point_);
146   if (size_t{chosen_device_index} >= devices_.size())
147     return;
148 
149   LogSharingSelectedIndex(controller_->GetFeatureMetricsPrefix(),
150                           kSharingUiContextMenu, chosen_device_index);
151 
152   controller_->OnDeviceSelected(phone_number_, *devices_[chosen_device_index],
153                                 *entry_point_);
154 }
155