Commit 14805446 authored by Tommy Steimel's avatar Tommy Steimel Committed by Commit Bot

GMC: Add backend IPH logic

This CL adds the Global Media Controls IPH triggering logic and
connects it with the frontend IPH bubble.

Bug: 991585
Change-Id: I3c7b1e824ede5b620a87fa92306f6c9bdac1e348
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1783667Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarCollin Baker <collinbaker@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Commit-Queue: Tommy Steimel <steimel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#694272}
parent 580da61b
...@@ -948,6 +948,10 @@ jumbo_split_static_library("ui") { ...@@ -948,6 +948,10 @@ jumbo_split_static_library("ui") {
"hung_renderer/hung_renderer_core.h", "hung_renderer/hung_renderer_core.h",
"in_product_help/active_tab_tracker.cc", "in_product_help/active_tab_tracker.cc",
"in_product_help/active_tab_tracker.h", "in_product_help/active_tab_tracker.h",
"in_product_help/global_media_controls_in_product_help.cc",
"in_product_help/global_media_controls_in_product_help.h",
"in_product_help/global_media_controls_in_product_help_factory.cc",
"in_product_help/global_media_controls_in_product_help_factory.h",
"in_product_help/in_product_help.h", "in_product_help/in_product_help.h",
"in_product_help/reopen_tab_in_product_help.cc", "in_product_help/reopen_tab_in_product_help.cc",
"in_product_help/reopen_tab_in_product_help.h", "in_product_help/reopen_tab_in_product_help.h",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/in_product_help/global_media_controls_in_product_help.h"
#include "chrome/browser/feature_engagement/tracker_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/in_product_help/in_product_help.h"
#include "components/feature_engagement/public/event_constants.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/feature_engagement/public/tracker.h"
GlobalMediaControlsInProductHelp::GlobalMediaControlsInProductHelp(
Profile* profile)
: profile_(profile) {
DCHECK(profile_);
BrowserList::AddObserver(this);
}
GlobalMediaControlsInProductHelp::~GlobalMediaControlsInProductHelp() {
BrowserList::RemoveObserver(this);
StopListening();
}
void GlobalMediaControlsInProductHelp::OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) {
// We only care about when the active tab has changed.
if (!selection.active_tab_changed())
return;
// If this is replacing the tab with media, then we don't want to show the
// IPH.
if (selection.reason == CHANGE_REASON_REPLACED)
return;
// If the last tab was playing media, then media was backgrounded.
if (current_tab_likely_playing_media_)
OnMediaBackgrounded();
}
void GlobalMediaControlsInProductHelp::OnBrowserClosing(Browser* browser) {
// If the window we're watching is closing, stop listening to it.
if (browser->tab_strip_model() == observed_tab_strip_model_)
StopListening();
}
void GlobalMediaControlsInProductHelp::OnBrowserSetLastActive(
Browser* browser) {
// If we're no longer on the same window, stop listening.
if (observed_tab_strip_model_ != browser->tab_strip_model())
StopListening();
}
void GlobalMediaControlsInProductHelp::ToolbarIconEnabled() {
// If the current window isn't for our profile, then the playing media that
// caused the toolbar icon to be enabled is definitely not in the foreground
// tab (since it can't be in this window). Therefore, we don't assume the
// current tab is playing media.
Browser* browser = BrowserList::GetInstance()->GetLastActive();
if (!browser || browser->profile() != profile_)
return;
// We're likely on a tab that's playing media. Watch for active tab changes.
current_tab_likely_playing_media_ = true;
// If we're already watching this window, do nothing.
if (observed_tab_strip_model_ == browser->tab_strip_model())
return;
// If we were watching a different one, stop.
if (observed_tab_strip_model_)
observed_tab_strip_model_->RemoveObserver(this);
// Watch the current one.
observed_tab_strip_model_ = browser->tab_strip_model();
observed_tab_strip_model_->AddObserver(this);
}
void GlobalMediaControlsInProductHelp::ToolbarIconDisabled() {
// Media has stopped playing. Stop watching for active tab changes.
StopListening();
}
void GlobalMediaControlsInProductHelp::GlobalMediaControlsOpened() {
GetTracker()->NotifyEvent(
feature_engagement::events::kGlobalMediaControlsOpened);
}
void GlobalMediaControlsInProductHelp::HelpDismissed() {
GetTracker()->Dismissed(feature_engagement::kIPHGlobalMediaControlsFeature);
}
void GlobalMediaControlsInProductHelp::OnMediaBackgrounded() {
StopListening();
GetTracker()->NotifyEvent(feature_engagement::events::kMediaBackgrounded);
if (GetTracker()->ShouldTriggerHelpUI(
feature_engagement::kIPHGlobalMediaControlsFeature)) {
auto* browser = BrowserList::GetInstance()->GetLastActive();
DCHECK(browser);
browser->window()->ShowInProductHelpPromo(
InProductHelpFeature::kGlobalMediaControls);
}
}
void GlobalMediaControlsInProductHelp::StopListening() {
current_tab_likely_playing_media_ = false;
if (observed_tab_strip_model_) {
observed_tab_strip_model_->RemoveObserver(this);
observed_tab_strip_model_ = nullptr;
}
}
feature_engagement::Tracker* GlobalMediaControlsInProductHelp::GetTracker() {
return feature_engagement::TrackerFactory::GetForBrowserContext(profile_);
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_IN_PRODUCT_HELP_GLOBAL_MEDIA_CONTROLS_IN_PRODUCT_HELP_H_
#define CHROME_BROWSER_UI_IN_PRODUCT_HELP_GLOBAL_MEDIA_CONTROLS_IN_PRODUCT_HELP_H_
#include "base/macros.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "components/keyed_service/core/keyed_service.h"
class Browser;
class Profile;
namespace feature_engagement {
class Tracker;
}
// Listens for the triggering conditions for the global media controls
// in-product help and starts the IPH flow at the appropriate time. This is a
// |Profile|-keyed service since we track interactions per user profile. Hooks
// throughout the browser UI code will fetch this service and notify it of
// interesting user actions.
class GlobalMediaControlsInProductHelp : public KeyedService,
public TabStripModelObserver,
public BrowserListObserver {
public:
explicit GlobalMediaControlsInProductHelp(Profile* profile);
~GlobalMediaControlsInProductHelp() override;
// TabStripModelObserver implementation.
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override;
// BrowserListObserver implementation.
void OnBrowserClosing(Browser* browser) override;
void OnBrowserSetLastActive(Browser* browser) override;
// Called when the Global Media Controls toolbar icon is enabled and shown.
void ToolbarIconEnabled();
// Called when the Global Media Controls toolbar icon is disabled or hidden.
void ToolbarIconDisabled();
// Called when the Global Media Controls dialog is opened.
void GlobalMediaControlsOpened();
// Must be called when IPH promo finishes showing, whether by use of the
// feature or by timing out.
void HelpDismissed();
private:
// Called when we're pretty sure that a tab playing controllable media has
// been backgrounded.
void OnMediaBackgrounded();
// Stops observing the tab strip for changes.
void StopListening();
feature_engagement::Tracker* GetTracker();
Profile* const profile_;
TabStripModel* observed_tab_strip_model_ = nullptr;
// True if the currently focused tab is probably playing media.
bool current_tab_likely_playing_media_ = false;
DISALLOW_COPY_AND_ASSIGN(GlobalMediaControlsInProductHelp);
};
#endif // CHROME_BROWSER_UI_IN_PRODUCT_HELP_GLOBAL_MEDIA_CONTROLS_IN_PRODUCT_HELP_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/in_product_help/global_media_controls_in_product_help_factory.h"
#include <memory>
#include "base/memory/singleton.h"
#include "chrome/browser/feature_engagement/tracker_factory.h"
#include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/in_product_help/global_media_controls_in_product_help.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
GlobalMediaControlsInProductHelpFactory::
GlobalMediaControlsInProductHelpFactory()
: BrowserContextKeyedServiceFactory(
"GlobalMediaControlsInProductHelp",
BrowserContextDependencyManager::GetInstance()) {
DependsOn(feature_engagement::TrackerFactory::GetInstance());
}
GlobalMediaControlsInProductHelpFactory::
~GlobalMediaControlsInProductHelpFactory() {}
// static
GlobalMediaControlsInProductHelpFactory*
GlobalMediaControlsInProductHelpFactory::GetInstance() {
return base::Singleton<GlobalMediaControlsInProductHelpFactory>::get();
}
// static
GlobalMediaControlsInProductHelp*
GlobalMediaControlsInProductHelpFactory::GetForProfile(Profile* profile) {
return static_cast<GlobalMediaControlsInProductHelp*>(
GetInstance()->GetServiceForBrowserContext(profile, true));
}
KeyedService* GlobalMediaControlsInProductHelpFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return new GlobalMediaControlsInProductHelp(
Profile::FromBrowserContext(context));
}
content::BrowserContext*
GlobalMediaControlsInProductHelpFactory::GetBrowserContextToUse(
content::BrowserContext* context) const {
return chrome::GetBrowserContextRedirectedInIncognito(context);
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_IN_PRODUCT_HELP_GLOBAL_MEDIA_CONTROLS_IN_PRODUCT_HELP_FACTORY_H_
#define CHROME_BROWSER_UI_IN_PRODUCT_HELP_GLOBAL_MEDIA_CONTROLS_IN_PRODUCT_HELP_FACTORY_H_
#include "base/macros.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
class Profile;
namespace base {
template <typename T>
struct DefaultSingletonTraits;
} // namespace base
namespace content {
class BrowserContext;
} // namespace content
class GlobalMediaControlsInProductHelp;
class GlobalMediaControlsInProductHelpFactory
: public BrowserContextKeyedServiceFactory {
public:
static GlobalMediaControlsInProductHelpFactory* GetInstance();
static GlobalMediaControlsInProductHelp* GetForProfile(Profile* profile);
private:
GlobalMediaControlsInProductHelpFactory();
~GlobalMediaControlsInProductHelpFactory() override;
// BrowserContextKeyedServiceFactory overrides:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const override;
content::BrowserContext* GetBrowserContextToUse(
content::BrowserContext* context) const override;
friend struct base::DefaultSingletonTraits<
GlobalMediaControlsInProductHelpFactory>;
DISALLOW_COPY_AND_ASSIGN(GlobalMediaControlsInProductHelpFactory);
};
#endif // CHROME_BROWSER_UI_IN_PRODUCT_HELP_GLOBAL_MEDIA_CONTROLS_IN_PRODUCT_HELP_FACTORY_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
#include <memory>
#include <string>
#include <utility>
#include "chrome/browser/ui/in_product_help/global_media_controls_in_product_help.h"
#include "base/bind.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/feature_engagement/tracker_factory.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "chrome/test/base/testing_profile.h"
#include "components/feature_engagement/public/event_constants.h"
#include "components/feature_engagement/public/feature_constants.h"
#include "components/feature_engagement/public/tracker.h"
#include "components/feature_engagement/test/mock_tracker.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::Return;
using MockTracker = ::testing::NiceMock<feature_engagement::test::MockTracker>;
class GlobalMediaControlsInProductHelpTest : public BrowserWithTestWindowTest {
protected:
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
scoped_feature_list_.InitAndEnableFeature(
feature_engagement::kIPHGlobalMediaControlsFeature);
}
// We want to use |MockTracker| instead of |Tracker|, so we must override its
// factory.
TestingProfile::TestingFactories GetTestingFactories() override {
return {{feature_engagement::TrackerFactory::GetInstance(),
base::BindRepeating(CreateTracker)}};
}
MockTracker* GetMockTracker() { return GetMockTrackerForProfile(profile()); }
MockTracker* GetMockTrackerForProfile(Profile* profile) {
return static_cast<MockTracker*>(
feature_engagement::TrackerFactory::GetForBrowserContext(profile));
}
private:
// Factory function for our |MockTracker|
static std::unique_ptr<KeyedService> CreateTracker(
content::BrowserContext* context) {
return std::make_unique<MockTracker>();
}
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(GlobalMediaControlsInProductHelpTest,
OpenNewTabWhileMediaPlayingTriggersIPH) {
GlobalMediaControlsInProductHelp gmc_iph(profile());
auto* mock_tracker = GetMockTracker();
EXPECT_CALL(*mock_tracker, ShouldTriggerHelpUI(_))
.Times(1)
.WillOnce(Return(true));
// Start playing media in a tab.
BrowserList::SetLastActive(browser());
AddTab(browser(), GURL("chrome://blank"));
gmc_iph.ToolbarIconEnabled();
// Open a new foreground tab.
AddTab(browser(), GURL("chrome://blank"));
}
TEST_F(GlobalMediaControlsInProductHelpTest,
OpenNewTabWhileMediaNotPlayingDoesntTriggerIPH) {
GlobalMediaControlsInProductHelp gmc_iph(profile());
auto* mock_tracker = GetMockTracker();
EXPECT_CALL(*mock_tracker, ShouldTriggerHelpUI(_)).Times(0);
// Have a tab with no media.
BrowserList::SetLastActive(browser());
AddTab(browser(), GURL("chrome://blank"));
// Open a new foreground tab.
AddTab(browser(), GURL("chrome://blank"));
}
TEST_F(GlobalMediaControlsInProductHelpTest, DoesNotTriggerForOtherProfiles) {
std::unique_ptr<BrowserWindow> alt_window = CreateBrowserWindow();
Profile* alt_profile = profile()->GetOffTheRecordProfile();
std::unique_ptr<Browser> alt_browser =
CreateBrowser(alt_profile, Browser::TYPE_NORMAL, false, alt_window.get());
GlobalMediaControlsInProductHelp gmc_iph(profile());
GlobalMediaControlsInProductHelp alt_gmc_iph(alt_profile);
// The original profile should not trigger the IPH.
auto* mock_tracker = GetMockTracker();
EXPECT_CALL(*mock_tracker, ShouldTriggerHelpUI(_)).Times(0);
// The alt one should.
auto* alt_mock_tracker = GetMockTrackerForProfile(alt_profile);
EXPECT_CALL(*alt_mock_tracker, ShouldTriggerHelpUI(_))
.Times(1)
.WillOnce(Return(true));
// Start playing media.
BrowserList::SetLastActive(browser());
AddTab(browser(), GURL("chrome://blank"));
gmc_iph.ToolbarIconEnabled();
// Switch to the other profile and play media.
BrowserList::SetLastActive(alt_browser.get());
AddTab(alt_browser.get(), GURL("chrome://blank"));
alt_gmc_iph.ToolbarIconEnabled();
// Open a new foreground tab to the other profile.
AddTab(alt_browser.get(), GURL("chrome://blank"));
// Need to manually close all tabs before |alt_browser| is destroyed or we'll
// crash.
alt_browser->tab_strip_model()->CloseAllTabs();
}
TEST_F(GlobalMediaControlsInProductHelpTest, MediaStoppedDoesNotTriggerIPH) {
GlobalMediaControlsInProductHelp gmc_iph(profile());
auto* mock_tracker = GetMockTracker();
EXPECT_CALL(*mock_tracker, ShouldTriggerHelpUI(_)).Times(0);
// Start playing media in a tab.
BrowserList::SetLastActive(browser());
AddTab(browser(), GURL("chrome://blank"));
gmc_iph.ToolbarIconEnabled();
// Stop playing media.
gmc_iph.ToolbarIconDisabled();
// Open a new foreground tab.
AddTab(browser(), GURL("chrome://blank"));
}
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
// Identifies a feature that has in-product help. // Identifies a feature that has in-product help.
enum class InProductHelpFeature { enum class InProductHelpFeature {
kGlobalMediaControls,
kIncognitoWindow, kIncognitoWindow,
kReopenTab, kReopenTab,
}; };
......
...@@ -26,9 +26,13 @@ void FeaturePromoBubbleTimeout::OnMouseEntered() { ...@@ -26,9 +26,13 @@ void FeaturePromoBubbleTimeout::OnMouseEntered() {
} }
void FeaturePromoBubbleTimeout::OnMouseExited() { void FeaturePromoBubbleTimeout::OnMouseExited() {
if (delay_short_.is_zero()) if (delay_short_.is_zero() && delay_default_.is_zero())
return; return;
StartAutoCloseTimer(delay_short_);
if (delay_short_.is_zero())
StartAutoCloseTimer(delay_default_);
else
StartAutoCloseTimer(delay_short_);
} }
void FeaturePromoBubbleTimeout::StartAutoCloseTimer( void FeaturePromoBubbleTimeout::StartAutoCloseTimer(
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <utility> #include <utility>
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/browser/ui/in_product_help/global_media_controls_in_product_help.h"
#include "chrome/browser/ui/in_product_help/global_media_controls_in_product_help_factory.h"
#include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_timeout.h" #include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_timeout.h"
#include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h" #include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/browser/ui/views/frame/browser_view.h"
...@@ -22,8 +24,12 @@ constexpr base::TimeDelta kPromoHideDelay = base::TimeDelta::FromSeconds(5); ...@@ -22,8 +24,12 @@ constexpr base::TimeDelta kPromoHideDelay = base::TimeDelta::FromSeconds(5);
} // anonymous namespace } // anonymous namespace
GlobalMediaControlsPromoController::GlobalMediaControlsPromoController( GlobalMediaControlsPromoController::GlobalMediaControlsPromoController(
BrowserView* browser_view) MediaToolbarButtonView* owner,
: browser_view_(browser_view) {} Profile* profile)
: owner_(owner), profile_(profile) {
DCHECK(owner_);
DCHECK(profile_);
}
void GlobalMediaControlsPromoController::ShowPromo() { void GlobalMediaControlsPromoController::ShowPromo() {
// This shouldn't be called more than once. Check that state is fresh. // This shouldn't be called more than once. Check that state is fresh.
...@@ -33,12 +39,10 @@ void GlobalMediaControlsPromoController::ShowPromo() { ...@@ -33,12 +39,10 @@ void GlobalMediaControlsPromoController::ShowPromo() {
DCHECK(!is_showing_); DCHECK(!is_showing_);
is_showing_ = true; is_showing_ = true;
auto* media_toolbar_button = browser_view_->toolbar()->media_button();
// This should never be called when the toolbar button is not visible and // This should never be called when the toolbar button is not visible and
// enabled. // enabled.
DCHECK(media_toolbar_button->GetVisible()); DCHECK(owner_->GetVisible());
DCHECK(media_toolbar_button->GetEnabled()); DCHECK(owner_->GetEnabled());
// Here, we open the promo bubble. // Here, we open the promo bubble.
// TODO(https://crbug.com/991585): Also highlight the toolbar button. // TODO(https://crbug.com/991585): Also highlight the toolbar button.
...@@ -49,16 +53,10 @@ void GlobalMediaControlsPromoController::ShowPromo() { ...@@ -49,16 +53,10 @@ void GlobalMediaControlsPromoController::ShowPromo() {
kPromoHideDelay, base::TimeDelta()); kPromoHideDelay, base::TimeDelta());
} }
// TODO(https://crbug.com/991585): Use IDS_GLOBAL_MEDIA_CONTROLS_PROMO here
// instead. The reason we're going to use this placeholder here for now is
// that the win10 builder fails the browser tests due to resource
// whitelisting. Since the string isn't used in production code yet, it isn't
// whitelisted and the browser test crashes.
//
// TODO(https://crbug.com/991585): Supply a screenreader string too. // TODO(https://crbug.com/991585): Supply a screenreader string too.
int string_specifier = IDS_GLOBAL_MEDIA_CONTROLS_ICON_TOOLTIP_TEXT; int string_specifier = IDS_GLOBAL_MEDIA_CONTROLS_PROMO;
promo_bubble_ = FeaturePromoBubbleView::CreateOwned( promo_bubble_ = FeaturePromoBubbleView::CreateOwned(
media_toolbar_button, views::BubbleBorder::Arrow::TOP_RIGHT, owner_, views::BubbleBorder::Arrow::TOP_RIGHT,
FeaturePromoBubbleView::ActivationAction::DO_NOT_ACTIVATE, FeaturePromoBubbleView::ActivationAction::DO_NOT_ACTIVATE,
string_specifier, base::nullopt, base::nullopt, string_specifier, base::nullopt, base::nullopt,
std::move(feature_promo_bubble_timeout)); std::move(feature_promo_bubble_timeout));
...@@ -92,6 +90,6 @@ void GlobalMediaControlsPromoController::FinishPromo() { ...@@ -92,6 +90,6 @@ void GlobalMediaControlsPromoController::FinishPromo() {
is_showing_ = false; is_showing_ = false;
// TODO(https://crbug.com/991585): Inform the IPH service that the dialog was GlobalMediaControlsInProductHelpFactory::GetForProfile(profile_)
// closed. ->HelpDismissed();
} }
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
#include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h" #include "chrome/browser/ui/views/feature_promos/feature_promo_bubble_view.h"
#include "ui/views/widget/widget_observer.h" #include "ui/views/widget/widget_observer.h"
class BrowserView; class MediaToolbarButtonView;
class Profile;
// Handles display of the global media controls in-product help promo, including // Handles display of the global media controls in-product help promo, including
// showing the promo bubble and highlighting the appropriate app menu items. // showing the promo bubble and highlighting the appropriate app menu items.
...@@ -16,7 +17,8 @@ class BrowserView; ...@@ -16,7 +17,8 @@ class BrowserView;
// finished. // finished.
class GlobalMediaControlsPromoController : public views::WidgetObserver { class GlobalMediaControlsPromoController : public views::WidgetObserver {
public: public:
explicit GlobalMediaControlsPromoController(BrowserView* browser_view); GlobalMediaControlsPromoController(MediaToolbarButtonView* owner,
Profile* profile);
~GlobalMediaControlsPromoController() override = default; ~GlobalMediaControlsPromoController() override = default;
// Shows the IPH promo. Should only be called once. // Shows the IPH promo. Should only be called once.
...@@ -42,7 +44,8 @@ class GlobalMediaControlsPromoController : public views::WidgetObserver { ...@@ -42,7 +44,8 @@ class GlobalMediaControlsPromoController : public views::WidgetObserver {
// was closed. // was closed.
void FinishPromo(); void FinishPromo();
BrowserView* const browser_view_; MediaToolbarButtonView* const owner_;
Profile* const profile_;
FeaturePromoBubbleView* promo_bubble_ = nullptr; FeaturePromoBubbleView* promo_bubble_ = nullptr;
// Whether we are showing the promo. // Whether we are showing the promo.
......
...@@ -26,7 +26,7 @@ class GlobalMediaControlsPromoControllerDialogBrowserTest ...@@ -26,7 +26,7 @@ class GlobalMediaControlsPromoControllerDialogBrowserTest
void SetUpOnMainThread() override { void SetUpOnMainThread() override {
promo_controller_ = std::make_unique<GlobalMediaControlsPromoController>( promo_controller_ = std::make_unique<GlobalMediaControlsPromoController>(
BrowserView::GetBrowserViewForBrowser(browser())); GetMediaToolbarButton(), browser()->profile());
} }
void ShowUi(const std::string& name) override { ShowPromo(); } void ShowUi(const std::string& name) override { ShowPromo(); }
...@@ -55,11 +55,15 @@ class GlobalMediaControlsPromoControllerDialogBrowserTest ...@@ -55,11 +55,15 @@ class GlobalMediaControlsPromoControllerDialogBrowserTest
} }
private: private:
MediaToolbarButtonView* GetMediaToolbarButton() {
return BrowserView::GetBrowserViewForBrowser(browser())
->toolbar()
->media_button();
}
// Forces the media toolbar button to appear so we can add the promo to it. // Forces the media toolbar button to appear so we can add the promo to it.
void ShowMediaToolbarButton() { void ShowMediaToolbarButton() {
auto* media_button = BrowserView::GetBrowserViewForBrowser(browser()) auto* media_button = GetMediaToolbarButton();
->toolbar()
->media_button();
media_button->Show(); media_button->Show();
media_button->Enable(); media_button->Enable();
} }
......
...@@ -96,6 +96,7 @@ ...@@ -96,6 +96,7 @@
#include "chrome/browser/ui/views/frame/web_contents_close_handler.h" #include "chrome/browser/ui/views/frame/web_contents_close_handler.h"
#include "chrome/browser/ui/views/frame/web_footer_experiment_view.h" #include "chrome/browser/ui/views/frame/web_footer_experiment_view.h"
#include "chrome/browser/ui/views/fullscreen_control/fullscreen_control_host.h" #include "chrome/browser/ui/views/fullscreen_control/fullscreen_control_host.h"
#include "chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h"
#include "chrome/browser/ui/views/hats/hats_bubble_view.h" #include "chrome/browser/ui/views/hats/hats_bubble_view.h"
#include "chrome/browser/ui/views/ime/ime_warning_bubble_view.h" #include "chrome/browser/ui/views/ime/ime_warning_bubble_view.h"
#include "chrome/browser/ui/views/infobars/infobar_container_view.h" #include "chrome/browser/ui/views/infobars/infobar_container_view.h"
...@@ -3089,8 +3090,17 @@ void BrowserView::ShowEmojiPanel() { ...@@ -3089,8 +3090,17 @@ void BrowserView::ShowEmojiPanel() {
} }
void BrowserView::ShowInProductHelpPromo(InProductHelpFeature iph_feature) { void BrowserView::ShowInProductHelpPromo(InProductHelpFeature iph_feature) {
if (iph_feature == InProductHelpFeature::kReopenTab) switch (iph_feature) {
reopen_tab_promo_controller_.ShowPromo(); case InProductHelpFeature::kIncognitoWindow:
break;
case InProductHelpFeature::kReopenTab:
reopen_tab_promo_controller_.ShowPromo();
break;
case InProductHelpFeature::kGlobalMediaControls:
if (toolbar_ && toolbar_->media_button())
toolbar_->media_button()->ShowPromo();
break;
}
} }
bool BrowserView::DoCutCopyPasteForWebContents( bool BrowserView::DoCutCopyPasteForWebContents(
......
...@@ -5,6 +5,10 @@ ...@@ -5,6 +5,10 @@
#include "chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h" #include "chrome/browser/ui/views/global_media_controls/media_toolbar_button_view.h"
#include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/in_product_help/global_media_controls_in_product_help.h"
#include "chrome/browser/ui/in_product_help/global_media_controls_in_product_help_factory.h"
#include "chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.h"
#include "chrome/browser/ui/views/global_media_controls/media_dialog_view.h" #include "chrome/browser/ui/views/global_media_controls/media_dialog_view.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "components/vector_icons/vector_icons.h" #include "components/vector_icons/vector_icons.h"
...@@ -16,10 +20,15 @@ ...@@ -16,10 +20,15 @@
MediaToolbarButtonView::MediaToolbarButtonView( MediaToolbarButtonView::MediaToolbarButtonView(
const base::UnguessableToken& source_id, const base::UnguessableToken& source_id,
service_manager::Connector* connector) service_manager::Connector* connector,
const Browser* browser)
: ToolbarButton(this), : ToolbarButton(this),
connector_(connector), connector_(connector),
controller_(source_id, connector_, this) { controller_(source_id, connector_, this),
browser_(browser) {
in_product_help_ = GlobalMediaControlsInProductHelpFactory::GetForProfile(
browser_->profile());
button_controller()->set_notify_action( button_controller()->set_notify_action(
views::ButtonController::NotifyAction::NOTIFY_ON_PRESS); views::ButtonController::NotifyAction::NOTIFY_ON_PRESS);
SetTooltipText( SetTooltipText(
...@@ -35,10 +44,12 @@ MediaToolbarButtonView::~MediaToolbarButtonView() = default; ...@@ -35,10 +44,12 @@ MediaToolbarButtonView::~MediaToolbarButtonView() = default;
void MediaToolbarButtonView::ButtonPressed(views::Button* sender, void MediaToolbarButtonView::ButtonPressed(views::Button* sender,
const ui::Event& event) { const ui::Event& event) {
if (MediaDialogView::IsShowing()) if (MediaDialogView::IsShowing()) {
MediaDialogView::HideDialog(); MediaDialogView::HideDialog();
else } else {
MediaDialogView::ShowDialog(this, &controller_, connector_); MediaDialogView::ShowDialog(this, &controller_, connector_);
InformIPHOfDialogShown();
}
} }
void MediaToolbarButtonView::Show() { void MediaToolbarButtonView::Show() {
...@@ -49,14 +60,17 @@ void MediaToolbarButtonView::Show() { ...@@ -49,14 +60,17 @@ void MediaToolbarButtonView::Show() {
void MediaToolbarButtonView::Hide() { void MediaToolbarButtonView::Hide() {
SetVisible(false); SetVisible(false);
PreferredSizeChanged(); PreferredSizeChanged();
InformIPHOfButtonDisabledorHidden();
} }
void MediaToolbarButtonView::Enable() { void MediaToolbarButtonView::Enable() {
SetEnabled(true); SetEnabled(true);
InformIPHOfButtonEnabled();
} }
void MediaToolbarButtonView::Disable() { void MediaToolbarButtonView::Disable() {
SetEnabled(false); SetEnabled(false);
InformIPHOfButtonDisabledorHidden();
} }
void MediaToolbarButtonView::UpdateIcon() { void MediaToolbarButtonView::UpdateIcon() {
...@@ -78,3 +92,35 @@ void MediaToolbarButtonView::UpdateIcon() { ...@@ -78,3 +92,35 @@ void MediaToolbarButtonView::UpdateIcon() {
SetImage(views::Button::STATE_DISABLED, SetImage(views::Button::STATE_DISABLED,
gfx::CreateVectorIcon(icon, dip_size, disabled_color)); gfx::CreateVectorIcon(icon, dip_size, disabled_color));
} }
void MediaToolbarButtonView::ShowPromo() {
GetPromoController().ShowPromo();
}
GlobalMediaControlsPromoController&
MediaToolbarButtonView::GetPromoController() {
if (!promo_controller_) {
promo_controller_ = std::make_unique<GlobalMediaControlsPromoController>(
this, browser_->profile());
}
return *promo_controller_;
}
void MediaToolbarButtonView::InformIPHOfDialogShown() {
if (in_product_help_)
in_product_help_->GlobalMediaControlsOpened();
GetPromoController().OnMediaDialogOpened();
}
void MediaToolbarButtonView::InformIPHOfButtonEnabled() {
if (in_product_help_)
in_product_help_->ToolbarIconEnabled();
}
void MediaToolbarButtonView::InformIPHOfButtonDisabledorHidden() {
if (in_product_help_)
in_product_help_->ToolbarIconDisabled();
GetPromoController().OnMediaToolbarButtonDisabledOrHidden();
}
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_TOOLBAR_BUTTON_VIEW_H_ #define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_TOOLBAR_BUTTON_VIEW_H_
#include "base/macros.h" #include "base/macros.h"
#include "base/unguessable_token.h"
#include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h" #include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h"
#include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller_delegate.h" #include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller_delegate.h"
#include "chrome/browser/ui/views/toolbar/toolbar_button.h" #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
...@@ -19,6 +18,10 @@ namespace service_manager { ...@@ -19,6 +18,10 @@ namespace service_manager {
class Connector; class Connector;
} // namespace service_manager } // namespace service_manager
class Browser;
class GlobalMediaControlsInProductHelp;
class GlobalMediaControlsPromoController;
// Media icon shown in the trusted area of toolbar. Its lifetime is tied to that // Media icon shown in the trusted area of toolbar. Its lifetime is tied to that
// of its parent ToolbarView. The icon is made visible when there is an active // of its parent ToolbarView. The icon is made visible when there is an active
// media session. // media session.
...@@ -26,8 +29,9 @@ class MediaToolbarButtonView : public ToolbarButton, ...@@ -26,8 +29,9 @@ class MediaToolbarButtonView : public ToolbarButton,
public MediaToolbarButtonControllerDelegate, public MediaToolbarButtonControllerDelegate,
public views::ButtonListener { public views::ButtonListener {
public: public:
explicit MediaToolbarButtonView(const base::UnguessableToken& source_id, MediaToolbarButtonView(const base::UnguessableToken& source_id,
service_manager::Connector* connector); service_manager::Connector* connector,
const Browser* browser);
~MediaToolbarButtonView() override; ~MediaToolbarButtonView() override;
// MediaToolbarButtonControllerDelegate implementation. // MediaToolbarButtonControllerDelegate implementation.
...@@ -42,9 +46,28 @@ class MediaToolbarButtonView : public ToolbarButton, ...@@ -42,9 +46,28 @@ class MediaToolbarButtonView : public ToolbarButton,
// Updates the icon image. // Updates the icon image.
void UpdateIcon(); void UpdateIcon();
void ShowPromo();
private: private:
// Lazily constructs |promo_controller_| if necessary.
GlobalMediaControlsPromoController& GetPromoController();
// Informs the Global Media Controls in-product help that the GMC dialog was
// opened.
void InformIPHOfDialogShown();
// Informs the Global Media Controls in-product help of the current button
// state.
void InformIPHOfButtonEnabled();
void InformIPHOfButtonDisabledorHidden();
// Shows the in-product help bubble.
std::unique_ptr<GlobalMediaControlsPromoController> promo_controller_;
service_manager::Connector* const connector_; service_manager::Connector* const connector_;
MediaToolbarButtonController controller_; MediaToolbarButtonController controller_;
const Browser* const browser_;
GlobalMediaControlsInProductHelp* in_product_help_;
DISALLOW_COPY_AND_ASSIGN(MediaToolbarButtonView); DISALLOW_COPY_AND_ASSIGN(MediaToolbarButtonView);
}; };
......
...@@ -251,7 +251,7 @@ void ToolbarView::Init() { ...@@ -251,7 +251,7 @@ void ToolbarView::Init() {
const base::UnguessableToken& source_id = const base::UnguessableToken& source_id =
content::MediaSession::GetSourceId(browser_->profile()); content::MediaSession::GetSourceId(browser_->profile());
media_button = std::make_unique<MediaToolbarButtonView>( media_button = std::make_unique<MediaToolbarButtonView>(
source_id, content::GetSystemConnector()); source_id, content::GetSystemConnector(), browser_);
} }
std::unique_ptr<ToolbarPageActionIconContainerView> std::unique_ptr<ToolbarPageActionIconContainerView>
......
...@@ -3790,6 +3790,7 @@ test("unit_tests") { ...@@ -3790,6 +3790,7 @@ test("unit_tests") {
"../browser/ui/global_media_controls/media_toolbar_button_controller_unittest.cc", "../browser/ui/global_media_controls/media_toolbar_button_controller_unittest.cc",
"../browser/ui/hid/hid_chooser_controller_unittest.cc", "../browser/ui/hid/hid_chooser_controller_unittest.cc",
"../browser/ui/in_product_help/active_tab_tracker_unittest.cc", "../browser/ui/in_product_help/active_tab_tracker_unittest.cc",
"../browser/ui/in_product_help/global_media_controls_in_product_help_unittest.cc",
"../browser/ui/in_product_help/reopen_tab_in_product_help_trigger_unittest.cc", "../browser/ui/in_product_help/reopen_tab_in_product_help_trigger_unittest.cc",
"../browser/ui/in_product_help/reopen_tab_in_product_help_unittest.cc", "../browser/ui/in_product_help/reopen_tab_in_product_help_unittest.cc",
"../browser/ui/omnibox/chrome_omnibox_navigation_observer_unittest.cc", "../browser/ui/omnibox/chrome_omnibox_navigation_observer_unittest.cc",
......
...@@ -22,6 +22,9 @@ const char kNewTabOpened[] = "new_tab_opened"; ...@@ -22,6 +22,9 @@ const char kNewTabOpened[] = "new_tab_opened";
const char kReopenTabConditionsMet[] = "reopen_tab_conditions_met"; const char kReopenTabConditionsMet[] = "reopen_tab_conditions_met";
const char kTabReopened[] = "tab_reopened"; const char kTabReopened[] = "tab_reopened";
const char kMediaBackgrounded[] = "media_backgrounded";
const char kGlobalMediaControlsOpened[] = "global_media_controls_opened";
const char kFocusModeOpened[] = "focus_mode_opened"; const char kFocusModeOpened[] = "focus_mode_opened";
const char kFocusModeConditionsMet[] = "focus_mode_conditions_met"; const char kFocusModeConditionsMet[] = "focus_mode_conditions_met";
......
...@@ -31,6 +31,12 @@ extern const char kReopenTabConditionsMet[]; ...@@ -31,6 +31,12 @@ extern const char kReopenTabConditionsMet[];
// The user reopened a previously closed tab. // The user reopened a previously closed tab.
extern const char kTabReopened[]; extern const char kTabReopened[];
// A tab with playing media was sent to the background.
extern const char kMediaBackgrounded[];
// The user opened the Global Media Controls dialog.
extern const char kGlobalMediaControlsOpened[];
// All the events declared below are the string names of deferred onboarding // All the events declared below are the string names of deferred onboarding
// events for the Focus Mode feature. // events for the Focus Mode feature.
......
...@@ -16,10 +16,12 @@ const base::Feature kIPHDummyFeature{"IPH_Dummy", ...@@ -16,10 +16,12 @@ const base::Feature kIPHDummyFeature{"IPH_Dummy",
#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
defined(OS_CHROMEOS) defined(OS_CHROMEOS)
const base::Feature kIPHReopenTabFeature{"IPH_ReopenTab",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHFocusModeFeature{"IPH_FocusMode", const base::Feature kIPHFocusModeFeature{"IPH_FocusMode",
base::FEATURE_DISABLED_BY_DEFAULT}; base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHGlobalMediaControlsFeature{
"IPH_GlobalMediaControls", base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kIPHReopenTabFeature{"IPH_ReopenTab",
base::FEATURE_DISABLED_BY_DEFAULT};
#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP) #if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
const base::Feature kIPHBookmarkFeature{"IPH_Bookmark", const base::Feature kIPHBookmarkFeature{"IPH_Bookmark",
......
...@@ -19,8 +19,9 @@ extern const base::Feature kIPHDummyFeature; ...@@ -19,8 +19,9 @@ extern const base::Feature kIPHDummyFeature;
#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
defined(OS_CHROMEOS) defined(OS_CHROMEOS)
extern const base::Feature kIPHReopenTabFeature;
extern const base::Feature kIPHFocusModeFeature; extern const base::Feature kIPHFocusModeFeature;
extern const base::Feature kIPHGlobalMediaControlsFeature;
extern const base::Feature kIPHReopenTabFeature;
#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP) #if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
extern const base::Feature kIPHBookmarkFeature; extern const base::Feature kIPHBookmarkFeature;
......
...@@ -59,8 +59,9 @@ const base::Feature* const kAllFeatures[] = { ...@@ -59,8 +59,9 @@ const base::Feature* const kAllFeatures[] = {
#endif // defined(OS_IOS) #endif // defined(OS_IOS)
#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
defined(OS_CHROMEOS) defined(OS_CHROMEOS)
&kIPHReopenTabFeature,
&kIPHFocusModeFeature, &kIPHFocusModeFeature,
&kIPHGlobalMediaControlsFeature,
&kIPHReopenTabFeature,
#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP) #if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
&kIPHBookmarkFeature, &kIPHBookmarkFeature,
&kIPHIncognitoWindowFeature, &kIPHIncognitoWindowFeature,
......
...@@ -109,8 +109,9 @@ DEFINE_VARIATION_PARAM(kIPHBadgedTranslateManualTriggerFeature, ...@@ -109,8 +109,9 @@ DEFINE_VARIATION_PARAM(kIPHBadgedTranslateManualTriggerFeature,
#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
defined(OS_CHROMEOS) defined(OS_CHROMEOS)
DEFINE_VARIATION_PARAM(kIPHReopenTabFeature, "IPH_ReopenTab");
DEFINE_VARIATION_PARAM(kIPHFocusModeFeature, "IPH_FocusMode"); DEFINE_VARIATION_PARAM(kIPHFocusModeFeature, "IPH_FocusMode");
DEFINE_VARIATION_PARAM(kIPHGlobalMediaControls, "IPH_GlobalMediaControls");
DEFINE_VARIATION_PARAM(kIPHReopenTabFeature, "IPH_ReopenTab");
#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP) #if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
DEFINE_VARIATION_PARAM(kIPHBookmarkFeature, "IPH_Bookmark"); DEFINE_VARIATION_PARAM(kIPHBookmarkFeature, "IPH_Bookmark");
DEFINE_VARIATION_PARAM(kIPHIncognitoWindowFeature, "IPH_IncognitoWindow"); DEFINE_VARIATION_PARAM(kIPHIncognitoWindowFeature, "IPH_IncognitoWindow");
...@@ -167,8 +168,9 @@ constexpr flags_ui::FeatureEntry::FeatureVariation ...@@ -167,8 +168,9 @@ constexpr flags_ui::FeatureEntry::FeatureVariation
VARIATION_ENTRY(kIPHBadgedTranslateManualTriggerFeature), VARIATION_ENTRY(kIPHBadgedTranslateManualTriggerFeature),
#elif defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \ #elif defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) || \
defined(OS_CHROMEOS) defined(OS_CHROMEOS)
VARIATION_ENTRY(kIPHReopenTabFeature),
VARIATION_ENTRY(kIPHFocusModeFeature), VARIATION_ENTRY(kIPHFocusModeFeature),
VARIATION_ENTRY(kIPHGlobalMediaControls),
VARIATION_ENTRY(kIPHReopenTabFeature),
#if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP) #if BUILDFLAG(ENABLE_LEGACY_DESKTOP_IN_PRODUCT_HELP)
VARIATION_ENTRY(kIPHBookmarkFeature), VARIATION_ENTRY(kIPHBookmarkFeature),
VARIATION_ENTRY(kIPHIncognitoWindowFeature), VARIATION_ENTRY(kIPHIncognitoWindowFeature),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment