summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormukai <mukai@chromium.org>2014-10-22 14:50:45 -0700
committerCommit bot <commit-bot@chromium.org>2014-10-22 21:50:57 +0000
commitc282de2f3a0f3484df5db8d2fe03e2acf9f3cc25 (patch)
treed41295a3f438b02a6aede4006610da196f665c89
parent451de88298a4bca3c8afd5db1faed46484147169 (diff)
downloadchromium_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.cc81
-rw-r--r--athena/extensions/public/extension_app_model_builder.h26
-rw-r--r--athena/home/app_list_view_delegate.cc2
-rw-r--r--athena/home/athena_start_page_view.cc61
-rw-r--r--athena/home/athena_start_page_view.h18
-rw-r--r--athena/home/athena_start_page_view_unittest.cc56
-rw-r--r--athena/home/public/app_model_builder.h5
-rw-r--r--athena/test/base/test_app_model_builder.cc2
-rw-r--r--athena/test/base/test_app_model_builder.h2
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);