diff options
author | mukai <mukai@chromium.org> | 2014-10-22 14:50:45 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-22 21:50:57 +0000 |
commit | c282de2f3a0f3484df5db8d2fe03e2acf9f3cc25 (patch) | |
tree | d41295a3f438b02a6aede4006610da196f665c89 | |
parent | 451de88298a4bca3c8afd5db1faed46484147169 (diff) | |
download | chromium_src-c282de2f3a0f3484df5db8d2fe03e2acf9f3cc25.zip chromium_src-c282de2f3a0f3484df5db8d2fe03e2acf9f3cc25.tar.gz chromium_src-c282de2f3a0f3484df5db8d2fe03e2acf9f3cc25.tar.bz2 |
Update the list of apps when installed/uninstalled for Athena.
BUG=425810
R=oshima@chromium.org
TEST=athena_unittests, manually
Review URL: https://codereview.chromium.org/665403005
Cr-Commit-Position: refs/heads/master@{#300768}
-rw-r--r-- | athena/extensions/extension_app_model_builder.cc | 81 | ||||
-rw-r--r-- | athena/extensions/public/extension_app_model_builder.h | 26 | ||||
-rw-r--r-- | athena/home/app_list_view_delegate.cc | 2 | ||||
-rw-r--r-- | athena/home/athena_start_page_view.cc | 61 | ||||
-rw-r--r-- | athena/home/athena_start_page_view.h | 18 | ||||
-rw-r--r-- | athena/home/athena_start_page_view_unittest.cc | 56 | ||||
-rw-r--r-- | athena/home/public/app_model_builder.h | 5 | ||||
-rw-r--r-- | athena/test/base/test_app_model_builder.cc | 2 | ||||
-rw-r--r-- | athena/test/base/test_app_model_builder.h | 2 |
9 files changed, 204 insertions, 49 deletions
diff --git a/athena/extensions/extension_app_model_builder.cc b/athena/extensions/extension_app_model_builder.cc index 92ee81f..411c8b8 100644 --- a/athena/extensions/extension_app_model_builder.cc +++ b/athena/extensions/extension_app_model_builder.cc @@ -8,6 +8,8 @@ #include "athena/activity/public/activity_manager.h" #include "athena/extensions/public/extensions_delegate.h" #include "extensions/browser/extension_icon_image.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_registry_factory.h" #include "extensions/common/constants.h" #include "extensions/common/extension_set.h" #include "extensions/common/manifest_handlers/icons_handler.h" @@ -29,17 +31,22 @@ class AppItem : public app_list::AppListItem { AppItem(scoped_refptr<const extensions::Extension> extension, content::BrowserContext* browser_context) : app_list::AppListItem(extension->id()), - extension_(extension), - browser_context_(browser_context), - icon_image_(browser_context_, - extension.get(), - extensions::IconsInfo::GetIcons(extension.get()), - extension_misc::EXTENSION_ICON_MEDIUM, - *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( - IDR_APP_DEFAULT_ICON), - NULL) { - icon_image_.image_skia().EnsureRepsForSupportedScales(); - SetIcon(icon_image_.image_skia(), false); + browser_context_(browser_context) { + Reload(extension); + } + + void Reload(scoped_refptr<const extensions::Extension> extension) { + extension_ = extension; + icon_image_.reset(new extensions::IconImage( + browser_context_, + extension.get(), + extensions::IconsInfo::GetIcons(extension.get()), + extension_misc::EXTENSION_ICON_MEDIUM, + *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( + IDR_APP_DEFAULT_ICON), + NULL)); + icon_image_->image_skia().EnsureRepsForSupportedScales(); + SetIcon(icon_image_->image_skia(), false); SetName(extension->name()); } @@ -51,7 +58,7 @@ class AppItem : public app_list::AppListItem { scoped_refptr<const extensions::Extension> extension_; content::BrowserContext* browser_context_; - extensions::IconImage icon_image_; + scoped_ptr<extensions::IconImage> icon_image_; DISALLOW_COPY_AND_ASSIGN(AppItem); }; @@ -60,27 +67,57 @@ class AppItem : public app_list::AppListItem { ExtensionAppModelBuilder::ExtensionAppModelBuilder( content::BrowserContext* browser_context) - : browser_context_(browser_context) { + : browser_context_(browser_context), model_(nullptr) { + extensions::ExtensionRegistryFactory::GetForBrowserContext(browser_context_) + ->AddObserver(this); } ExtensionAppModelBuilder::~ExtensionAppModelBuilder() { + extensions::ExtensionRegistryFactory::GetForBrowserContext(browser_context_) + ->RemoveObserver(this); } -void ExtensionAppModelBuilder::PopulateApps(app_list::AppListModel* model) { +void ExtensionAppModelBuilder::RegisterAppListModel( + app_list::AppListModel* model) { + DCHECK(!model_); + model_ = model; + ExtensionsDelegate* bridge = ExtensionsDelegate::Get(browser_context_); const extensions::ExtensionSet& extensions = bridge->GetInstalledExtensions(); for (extensions::ExtensionSet::const_iterator iter = extensions.begin(); iter != extensions.end(); ++iter) { - // Chrome icon is currently disabled for homecard since it's not meaningful. - // http://crbug.com/421677 - // TODO(mukai): use chrome/browser/extension_ui_util. - if ((*iter)->ShouldDisplayInAppLauncher() && - (*iter)->id() != kChromeAppId) { - model->AddItem(scoped_ptr<app_list::AppListItem>( - new AppItem(*iter, browser_context_))); - } + AddItem(*iter); + } +} + +void ExtensionAppModelBuilder::AddItem( + scoped_refptr<const extensions::Extension> extension) { + // Chrome icon is currently disabled for homecard since it's not meaningful. + // http://crbug.com/421677 + // TODO(mukai): use chrome/browser/extension_ui_util. + if (extension->ShouldDisplayInAppLauncher() && + extension->id() != kChromeAppId) { + model_->AddItem(make_scoped_ptr(new AppItem(extension, browser_context_))); } } +void ExtensionAppModelBuilder::OnExtensionInstalled( + content::BrowserContext* browser_context, + const extensions::Extension* extension, + bool is_update) { + app_list::AppListItem* item = model_->FindItem(extension->id()); + if (item) + static_cast<AppItem*>(item)->Reload(make_scoped_refptr(extension)); + else + AddItem(make_scoped_refptr(extension)); +} + +void ExtensionAppModelBuilder::OnExtensionUninstalled( + content::BrowserContext* browser_context, + const extensions::Extension* extension, + extensions::UninstallReason reason) { + model_->DeleteItem(extension->id()); +} + } // namespace athena diff --git a/athena/extensions/public/extension_app_model_builder.h b/athena/extensions/public/extension_app_model_builder.h index 0906add..abd8972 100644 --- a/athena/extensions/public/extension_app_model_builder.h +++ b/athena/extensions/public/extension_app_model_builder.h @@ -7,23 +7,45 @@ #include "athena/home/public/app_model_builder.h" #include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "extensions/browser/extension_registry_observer.h" namespace content { class BrowserContext; } +namespace extensions { +class Extension; +} + namespace athena { -class ATHENA_EXPORT ExtensionAppModelBuilder : public AppModelBuilder { +class ATHENA_EXPORT ExtensionAppModelBuilder + : public AppModelBuilder, + public extensions::ExtensionRegistryObserver { public: explicit ExtensionAppModelBuilder(content::BrowserContext* browser_context); virtual ~ExtensionAppModelBuilder(); - virtual void PopulateApps(app_list::AppListModel* model) override; + virtual void RegisterAppListModel(app_list::AppListModel* model) override; private: + void AddItem(scoped_refptr<const extensions::Extension> extension); + + // extensions::ExtensionRegistryObserver: + virtual void OnExtensionInstalled(content::BrowserContext* browser_context, + const extensions::Extension* extension, + bool is_update) override; + virtual void OnExtensionUninstalled( + content::BrowserContext* browser_context, + const extensions::Extension* extension, + extensions::UninstallReason reason) override; + content::BrowserContext* browser_context_; + // Unowned pointer to the app list model. + app_list::AppListModel* model_; + DISALLOW_COPY_AND_ASSIGN(ExtensionAppModelBuilder); }; diff --git a/athena/home/app_list_view_delegate.cc b/athena/home/app_list_view_delegate.cc index bb1e0be..eead245 100644 --- a/athena/home/app_list_view_delegate.cc +++ b/athena/home/app_list_view_delegate.cc @@ -34,7 +34,7 @@ AppListViewDelegate::AppListViewDelegate( SearchControllerFactory* search_factory) : model_(new app_list::AppListModel), speech_ui_(new app_list::SpeechUIModel) { - model_builder->PopulateApps(model_.get()); + model_builder->RegisterAppListModel(model_.get()); model_->search_box()->SetHintText( l10n_util::GetStringUTF16(IDS_ATHENA_SEARCH_BOX_HINT)); if (search_factory) { diff --git a/athena/home/athena_start_page_view.cc b/athena/home/athena_start_page_view.cc index 0891487..72ba721 100644 --- a/athena/home/athena_start_page_view.cc +++ b/athena/home/athena_start_page_view.cc @@ -84,8 +84,12 @@ class AppIconButton : public views::ImageButton, public views::ButtonListener { public: explicit AppIconButton(app_list::AppListItem* item) - : ImageButton(this), - item_(item) { + : ImageButton(this), item_(nullptr) { + SetItem(item); + } + + void SetItem(app_list::AppListItem* item) { + item_ = item; // TODO(mukai): icon should be resized. SetImage(STATE_NORMAL, &item->icon()); } @@ -209,11 +213,9 @@ AthenaStartPageView::AthenaStartPageView( app_icon_container_->layer()->SetFillsBoundsOpaquely(false); app_icon_container_->SetLayoutManager(new views::BoxLayout( views::BoxLayout::kHorizontal, 0, 0, kIconMargin)); - app_list::AppListItemList* top_level = - view_delegate->GetModel()->top_level_item_list(); - for (size_t i = 0; i < std::min(top_level->item_count(), kMaxIconNum); ++i) - app_icon_container_->AddChildView(new AppIconButton(top_level->item_at(i))); app_icon_container_->SetSize(GetIconContainerSize()); + UpdateAppIcons(); + view_delegate->GetModel()->top_level_item_list()->AddObserver(this); control_icon_container_ = new views::View(); control_icon_container_->SetPaintToLayer(true); @@ -235,7 +237,9 @@ AthenaStartPageView::AthenaStartPageView( AddChildView(search_box_container_); } -AthenaStartPageView::~AthenaStartPageView() {} +AthenaStartPageView::~AthenaStartPageView() { + delegate_->GetModel()->top_level_item_list()->RemoveObserver(this); +} void AthenaStartPageView::RequestFocusOnSearchBox() { search_box_view_->search_box()->RequestFocus(); @@ -324,6 +328,28 @@ AthenaStartPageView::LayoutData AthenaStartPageView::CreateCenteredBounds( return state; } +void AthenaStartPageView::UpdateAppIcons() { + app_list::AppListItemList* top_level = + delegate_->GetModel()->top_level_item_list(); + size_t max_items = std::min(top_level->item_count(), kMaxIconNum); + for (size_t i = 0; i < max_items; ++i) { + if (i < static_cast<size_t>(app_icon_container_->child_count())) { + AppIconButton* button = + static_cast<AppIconButton*>(app_icon_container_->child_at(i)); + button->SetItem(top_level->item_at(i)); + } else { + app_icon_container_->AddChildView( + new AppIconButton(top_level->item_at(i))); + } + } + + while (max_items < static_cast<size_t>(app_icon_container_->child_count())) { + scoped_ptr<views::View> remover( + app_icon_container_->child_at(app_icon_container_->child_count() - 1)); + app_icon_container_->RemoveChildView(remover.get()); + } +} + void AthenaStartPageView::LayoutSearchResults(bool should_show_search_results) { if (should_show_search_results == search_results_view_->layer()->GetTargetVisibility()) { @@ -445,4 +471,25 @@ void AthenaStartPageView::QueryChanged(app_list::SearchBoxView* sender) { LayoutSearchResults(!query.empty()); } +void AthenaStartPageView::OnListItemAdded(size_t index, + app_list::AppListItem* item) { + UpdateAppIcons(); +} + +void AthenaStartPageView::OnListItemRemoved(size_t index, + app_list::AppListItem* item) { + UpdateAppIcons(); +} + +void AthenaStartPageView::OnListItemMoved(size_t from_index, + size_t to_index, + app_list::AppListItem* item) { + UpdateAppIcons(); +} + +// static +size_t AthenaStartPageView::GetMaxIconNumForTest() { + return kMaxIconNum; +} + } // namespace athena diff --git a/athena/home/athena_start_page_view.h b/athena/home/athena_start_page_view.h index f92a73f..4e0f19d 100644 --- a/athena/home/athena_start_page_view.h +++ b/athena/home/athena_start_page_view.h @@ -8,11 +8,13 @@ #include "athena/athena_export.h" #include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "ui/app_list/app_list_item_list_observer.h" #include "ui/app_list/views/search_box_view_delegate.h" #include "ui/gfx/animation/tween.h" #include "ui/views/view.h" namespace app_list { +class AppListModelObserver; class AppListViewDelegate; class SearchBoxView; class SearchResultListView; @@ -22,7 +24,8 @@ namespace athena { class ATHENA_EXPORT AthenaStartPageView : public views::View, - public app_list::SearchBoxViewDelegate { + public app_list::SearchBoxViewDelegate, + public app_list::AppListItemListObserver { public: class Observer { public: @@ -51,6 +54,8 @@ class ATHENA_EXPORT AthenaStartPageView static const char kViewClassName[]; + static size_t GetMaxIconNumForTest(); + // A struct which bundles the layout data of subviews. struct LayoutData { gfx::Rect search_box; @@ -69,6 +74,8 @@ class ATHENA_EXPORT AthenaStartPageView // Returns the bounds for |VISIBLE_CENTERED|. LayoutData CreateCenteredBounds(int width); + void UpdateAppIcons(); + // Schedules the animation for the layout the search box and the search // results. void LayoutSearchResults(bool should_show_search_results); @@ -84,6 +91,15 @@ class ATHENA_EXPORT AthenaStartPageView // app_list::SearchBoxViewDelegate: virtual void QueryChanged(app_list::SearchBoxView* sender) override; + // app_list::AppListItemListObserver: + virtual void OnListItemAdded(size_t index, + app_list::AppListItem* item) override; + virtual void OnListItemRemoved(size_t index, + app_list::AppListItem* item) override; + virtual void OnListItemMoved(size_t from_index, + size_t to_index, + app_list::AppListItem* item) override; + // Not owned. app_list::AppListViewDelegate* delegate_; diff --git a/athena/home/athena_start_page_view_unittest.cc b/athena/home/athena_start_page_view_unittest.cc index c91177e..d611c3b 100644 --- a/athena/home/athena_start_page_view_unittest.cc +++ b/athena/home/athena_start_page_view_unittest.cc @@ -19,13 +19,6 @@ namespace athena { -namespace { - -// The number of dummy applications in this tetst. -const size_t kNumApps = 10; - -} - class AthenaTestViewDelegate : public app_list::test::AppListTestViewDelegate { public: AthenaTestViewDelegate() {} @@ -48,11 +41,8 @@ class AthenaStartPageViewTest : public test::AthenaTestBase { // testing::Test: virtual void SetUp() override { test::AthenaTestBase::SetUp(); - app_list::test::AppListTestModel* model = view_delegate_.GetTestModel(); - for (size_t i = 0; i < kNumApps; ++i) { - model->AddItem(new app_list::test::AppListTestModel::AppListTestItem( - base::StringPrintf("id-%" PRIuS, i), model)); - } + for (size_t i = 0; i < GetMaxIconNum(); ++i) + AddTestItem(i); view_.reset(new AthenaStartPageView(&view_delegate_)); SetSize(gfx::Size(1280, 800)); @@ -68,6 +58,24 @@ class AthenaStartPageViewTest : public test::AthenaTestBase { view_->Layout(); } + void AddTestItem(size_t index) { + app_list::test::AppListTestModel* model = view_delegate_.GetTestModel(); + model->AddItem(new app_list::test::AppListTestModel::AppListTestItem( + GetAppIdFor(index), model)); + } + + static size_t GetMaxIconNum() { + return AthenaStartPageView::GetMaxIconNumForTest(); + } + + static std::string GetAppIdFor(size_t index) { + return base::StringPrintf("id-%" PRIuS, index); + } + + app_list::AppListModel* GetModel() { return view_delegate_.GetTestModel(); } + + views::View* GetIconsContainer() { return view_->app_icon_container_; } + gfx::Rect GetIconsBounds() const { return view_->app_icon_container_->layer()->GetTargetBounds(); } @@ -244,4 +252,28 @@ TEST_F(AthenaStartPageViewTest, SearchFromBottom) { EXPECT_EQ(1.0f, layout_state()); } +TEST_F(AthenaStartPageViewTest, AppAddRemove) { + gfx::Rect icons_bounds = GetIconsBounds(); + EXPECT_EQ(GetMaxIconNum(), + static_cast<size_t>(GetIconsContainer()->child_count())); + + GetModel()->DeleteItem(GetAppIdFor(1)); + + // The removed icon disappear, however its bound should not change. + EXPECT_EQ(GetMaxIconNum() - 1, + static_cast<size_t>(GetIconsContainer()->child_count())); + EXPECT_EQ(icons_bounds.size().ToString(), GetIconsBounds().size().ToString()); + + AddTestItem(GetMaxIconNum() + 1); + EXPECT_EQ(GetMaxIconNum(), + static_cast<size_t>(GetIconsContainer()->child_count())); + EXPECT_EQ(icons_bounds.size().ToString(), GetIconsBounds().size().ToString()); + + // Adding more doesn't cause any effects. + AddTestItem(GetMaxIconNum() + 2); + EXPECT_EQ(GetMaxIconNum(), + static_cast<size_t>(GetIconsContainer()->child_count())); + EXPECT_EQ(icons_bounds.size().ToString(), GetIconsBounds().size().ToString()); +} + } // namespace athena diff --git a/athena/home/public/app_model_builder.h b/athena/home/public/app_model_builder.h index 5cfa890..50c38bf 100644 --- a/athena/home/public/app_model_builder.h +++ b/athena/home/public/app_model_builder.h @@ -20,8 +20,9 @@ class ATHENA_EXPORT AppModelBuilder { public: virtual ~AppModelBuilder() {} - // Fills |model| with the currently available app_list::AppListItems. - virtual void PopulateApps(app_list::AppListModel* model) = 0; + // Registers |model| to the builder so that the builder can fill the currently + // available app_list::AppListItems. + virtual void RegisterAppListModel(app_list::AppListModel* model) = 0; }; } // namespace athena diff --git a/athena/test/base/test_app_model_builder.cc b/athena/test/base/test_app_model_builder.cc index 6f2c37e..beaa846 100644 --- a/athena/test/base/test_app_model_builder.cc +++ b/athena/test/base/test_app_model_builder.cc @@ -96,7 +96,7 @@ TestAppModelBuilder::TestAppModelBuilder() { TestAppModelBuilder::~TestAppModelBuilder() { } -void TestAppModelBuilder::PopulateApps(app_list::AppListModel* model) { +void TestAppModelBuilder::RegisterAppListModel(app_list::AppListModel* model) { for (int i = 0; i < static_cast<int>(DummyItem::LAST_TYPE); ++i) { model->AddItem(scoped_ptr<app_list::AppListItem>( new DummyItem(static_cast<DummyItem::Type>(i)))); diff --git a/athena/test/base/test_app_model_builder.h b/athena/test/base/test_app_model_builder.h index 262ce25..e7a7e4b 100644 --- a/athena/test/base/test_app_model_builder.h +++ b/athena/test/base/test_app_model_builder.h @@ -16,7 +16,7 @@ class TestAppModelBuilder : public AppModelBuilder { virtual ~TestAppModelBuilder(); // Overridden from AppModelBuilder: - virtual void PopulateApps(app_list::AppListModel* model) override; + virtual void RegisterAppListModel(app_list::AppListModel* model) override; private: DISALLOW_COPY_AND_ASSIGN(TestAppModelBuilder); |