Experimental app list: Added "All apps" button on start page.

This replaces the fifth app icon on the start page. Clicking it
transitions to the apps grid view.

Currently has no icon, and it's a simple slide transition, but
eventually it will have a folder-like icon with a folder open animation.

BUG=425444

Review URL: https://codereview.chromium.org/665233002

Cr-Commit-Position: refs/heads/master@{#301778}
diff --git a/ui/app_list/BUILD.gn b/ui/app_list/BUILD.gn
index 6ebb5da..d95d6c1 100644
--- a/ui/app_list/BUILD.gn
+++ b/ui/app_list/BUILD.gn
@@ -95,6 +95,8 @@
 
   if (toolkit_views) {
     sources += [
+      "views/all_apps_tile_item_view.cc",
+      "views/all_apps_tile_item_view.h",
       "views/apps_container_view.cc",
       "views/apps_container_view.h",
       "views/app_list_background.cc",
@@ -146,6 +148,8 @@
       "views/speech_view.h",
       "views/start_page_view.cc",
       "views/start_page_view.h",
+      "views/search_result_tile_item_view.cc",
+      "views/search_result_tile_item_view.h",
       "views/tile_item_view.cc",
       "views/tile_item_view.h",
       "views/top_icon_animation_view.cc",
diff --git a/ui/app_list/app_list.gyp b/ui/app_list/app_list.gyp
index 01c878c8..7f3b72e 100644
--- a/ui/app_list/app_list.gyp
+++ b/ui/app_list/app_list.gyp
@@ -112,6 +112,8 @@
         'speech_ui_model.cc',
         'speech_ui_model.h',
         'speech_ui_model_observer.h',
+        'views/all_apps_tile_item_view.h',
+        'views/all_apps_tile_item_view.cc',
         'views/apps_container_view.cc',
         'views/apps_container_view.h',
         'views/app_list_background.cc',
@@ -157,6 +159,8 @@
         'views/search_result_list_view.cc',
         'views/search_result_list_view.h',
         'views/search_result_list_view_delegate.h',
+        'views/search_result_tile_item_view.cc',
+        'views/search_result_tile_item_view.h',
         'views/search_result_view.cc',
         'views/search_result_view.h',
         'views/speech_view.cc',
diff --git a/ui/app_list/views/all_apps_tile_item_view.cc b/ui/app_list/views/all_apps_tile_item_view.cc
new file mode 100644
index 0000000..acb527b1
--- /dev/null
+++ b/ui/app_list/views/all_apps_tile_item_view.cc
@@ -0,0 +1,28 @@
+// Copyright 2014 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 "ui/app_list/views/all_apps_tile_item_view.h"
+
+#include "ui/app_list/views/contents_view.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/strings/grit/ui_strings.h"
+
+namespace app_list {
+
+AllAppsTileItemView::AllAppsTileItemView(ContentsView* contents_view)
+    : contents_view_(contents_view) {
+  SetTitle(l10n_util::GetStringUTF16(IDS_APP_LIST_ALL_APPS));
+  // TODO(mgiuca): Set the button's icon.
+}
+
+AllAppsTileItemView::~AllAppsTileItemView() {
+}
+
+void AllAppsTileItemView::ButtonPressed(views::Button* sender,
+                                        const ui::Event& event) {
+  contents_view_->SetActivePage(
+      contents_view_->GetPageIndexForState(AppListModel::STATE_APPS));
+}
+
+}  // namespace app_list
diff --git a/ui/app_list/views/all_apps_tile_item_view.h b/ui/app_list/views/all_apps_tile_item_view.h
new file mode 100644
index 0000000..da3a3ad
--- /dev/null
+++ b/ui/app_list/views/all_apps_tile_item_view.h
@@ -0,0 +1,32 @@
+// Copyright 2014 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 UI_APP_LIST_VIEWS_ALL_APPS_TILE_ITEM_VIEW_H_
+#define UI_APP_LIST_VIEWS_ALL_APPS_TILE_ITEM_VIEW_H_
+
+#include <vector>
+
+#include "ui/app_list/views/tile_item_view.h"
+
+namespace app_list {
+
+class ContentsView;
+
+// A tile item for the "All apps" button on the start page.
+class AllAppsTileItemView : public TileItemView {
+ public:
+  AllAppsTileItemView(ContentsView* contents_view);
+
+  ~AllAppsTileItemView() override;
+
+  // TileItemView overrides:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+ private:
+  ContentsView* contents_view_;
+};
+
+}  // namespace app_list
+
+#endif  // UI_APP_LIST_VIEWS_ALL_APPS_TILE_ITEM_VIEW_H_
diff --git a/ui/app_list/views/app_list_view_unittest.cc b/ui/app_list/views/app_list_view_unittest.cc
index 3495e6a3c..901bcf2 100644
--- a/ui/app_list/views/app_list_view_unittest.cc
+++ b/ui/app_list/views/app_list_view_unittest.cc
@@ -23,6 +23,7 @@
 #include "ui/app_list/views/contents_view.h"
 #include "ui/app_list/views/search_box_view.h"
 #include "ui/app_list/views/search_result_list_view.h"
+#include "ui/app_list/views/search_result_tile_item_view.h"
 #include "ui/app_list/views/start_page_view.h"
 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
 #include "ui/app_list/views/tile_item_view.h"
@@ -44,17 +45,30 @@
   TEST_TYPE_END,
 };
 
-size_t GetVisibleTileItemViews(const std::vector<TileItemView*>& tiles) {
+template <class T>
+size_t GetVisibleViews(const std::vector<T*>& tiles) {
   size_t count = 0;
-  for (std::vector<TileItemView*>::const_iterator it = tiles.begin();
-       it != tiles.end();
-       ++it) {
-    if ((*it)->visible())
+  for (const auto& tile : tiles) {
+    if (tile->visible())
       count++;
   }
   return count;
 }
 
+void SimulateClick(views::View* view) {
+  gfx::Point center = view->GetLocalBounds().CenterPoint();
+  view->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED,
+                                      center,
+                                      center,
+                                      ui::EF_LEFT_MOUSE_BUTTON,
+                                      ui::EF_LEFT_MOUSE_BUTTON));
+  view->OnMouseReleased(ui::MouseEvent(ui::ET_MOUSE_RELEASED,
+                                       center,
+                                       center,
+                                       ui::EF_LEFT_MOUSE_BUTTON,
+                                       ui::EF_LEFT_MOUSE_BUTTON));
+}
+
 // Choose a set that is 3 regular app list pages and 2 landscape app list pages.
 const int kInitialItems = 34;
 
@@ -115,6 +129,9 @@
   // ensure all launcher pages are in the correct position.
   void ShowContentsViewPageAndVerify(AppListModel::State state);
 
+  // Tests that the app list is in |state|.
+  void VerifyPageActive(AppListModel::State state);
+
   // Shows the app list and waits until a paint occurs.
   void Show();
 
@@ -204,6 +221,12 @@
   int index = contents_view->GetPageIndexForState(state);
   contents_view->SetActivePage(index);
   contents_view->Layout();
+  VerifyPageActive(state);
+}
+
+void AppListViewTestContext::VerifyPageActive(AppListModel::State state) {
+  ContentsView* contents_view = view_->app_list_main_view()->contents_view();
+  int index = contents_view->GetPageIndexForState(state);
   for (int i = 0; i < contents_view->NumLauncherPages(); ++i) {
     EXPECT_EQ(i == index,
               contents_view->GetDefaultContentsBounds() ==
@@ -346,9 +369,13 @@
     // Show the start page view.
     ShowContentsViewPageAndVerify(AppListModel::STATE_START);
     EXPECT_FALSE(main_view->search_box_view()->visible());
-
     gfx::Size view_size(view_->GetPreferredSize());
-    ShowContentsViewPageAndVerify(AppListModel::STATE_APPS);
+
+    // Simulate clicking the "All apps" button. Check that we navigate to the
+    // apps grid view.
+    SimulateClick(start_page_view->all_apps_button());
+    main_view->contents_view()->Layout();
+    VerifyPageActive(AppListModel::STATE_APPS);
     EXPECT_TRUE(main_view->search_box_view()->visible());
 
     // Hiding and showing the search box should not affect the app list's
@@ -358,10 +385,10 @@
     // Check tiles hide and show on deletion and addition.
     model->results()->Add(new TestTileSearchResult());
     start_page_view->UpdateForTesting();
-    EXPECT_EQ(1u, GetVisibleTileItemViews(start_page_view->tile_views()));
+    EXPECT_EQ(1u, GetVisibleViews(start_page_view->tile_views()));
     model->results()->DeleteAll();
     start_page_view->UpdateForTesting();
-    EXPECT_EQ(0u, GetVisibleTileItemViews(start_page_view->tile_views()));
+    EXPECT_EQ(0u, GetVisibleViews(start_page_view->tile_views()));
   } else {
     EXPECT_EQ(NULL, start_page_view);
   }
@@ -455,7 +482,7 @@
   delegate_->GetTestModel()->results()->Add(new TestTileSearchResult());
   if (test_type_ == EXPERIMENTAL) {
     start_page_view->UpdateForTesting();
-    EXPECT_EQ(1u, GetVisibleTileItemViews(start_page_view->tile_views()));
+    EXPECT_EQ(1u, GetVisibleViews(start_page_view->tile_views()));
   }
 
   // Old model updates should be ignored.
@@ -463,7 +490,7 @@
   original_test_model->results()->Add(new TestTileSearchResult());
   if (test_type_ == EXPERIMENTAL) {
     start_page_view->UpdateForTesting();
-    EXPECT_EQ(1u, GetVisibleTileItemViews(start_page_view->tile_views()));
+    EXPECT_EQ(1u, GetVisibleViews(start_page_view->tile_views()));
   }
 
   Close();
diff --git a/ui/app_list/views/search_result_tile_item_view.cc b/ui/app_list/views/search_result_tile_item_view.cc
new file mode 100644
index 0000000..59507d7
--- /dev/null
+++ b/ui/app_list/views/search_result_tile_item_view.cc
@@ -0,0 +1,58 @@
+// Copyright 2014 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 "ui/app_list/views/search_result_tile_item_view.h"
+
+#include "ui/app_list/search_result.h"
+
+namespace app_list {
+
+SearchResultTileItemView::SearchResultTileItemView() : item_(NULL) {
+}
+
+SearchResultTileItemView::~SearchResultTileItemView() {
+  if (item_)
+    item_->RemoveObserver(this);
+}
+
+void SearchResultTileItemView::SetSearchResult(SearchResult* item) {
+  SetVisible(item != NULL);
+
+  SearchResult* old_item = item_;
+  if (old_item)
+    old_item->RemoveObserver(this);
+
+  item_ = item;
+
+  if (!item)
+    return;
+
+  item_->AddObserver(this);
+
+  SetTitle(item_->title());
+
+  // Only refresh the icon if it's different from the old one. This prevents
+  // flickering.
+  if (old_item == NULL ||
+      !item->icon().BackedBySameObjectAs(old_item->icon())) {
+    OnIconChanged();
+  }
+}
+
+void SearchResultTileItemView::ButtonPressed(views::Button* sender,
+                                             const ui::Event& event) {
+  item_->Open(event.flags());
+}
+
+void SearchResultTileItemView::OnIconChanged() {
+  SetIcon(item_->icon());
+}
+
+void SearchResultTileItemView::OnResultDestroying() {
+  if (item_)
+    item_->RemoveObserver(this);
+  item_ = NULL;
+}
+
+}  // namespace app_list
diff --git a/ui/app_list/views/search_result_tile_item_view.h b/ui/app_list/views/search_result_tile_item_view.h
new file mode 100644
index 0000000..149b3b5d
--- /dev/null
+++ b/ui/app_list/views/search_result_tile_item_view.h
@@ -0,0 +1,40 @@
+// Copyright 2014 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 UI_APP_LIST_VIEWS_SEARCH_RESULT_TILE_ITEM_VIEW_H_
+#define UI_APP_LIST_VIEWS_SEARCH_RESULT_TILE_ITEM_VIEW_H_
+
+#include "ui/app_list/search_result_observer.h"
+#include "ui/app_list/views/tile_item_view.h"
+
+namespace app_list {
+
+class SearchResult;
+
+// A TileItemView that displays a search result.
+class APP_LIST_EXPORT SearchResultTileItemView : public TileItemView,
+                                                 public SearchResultObserver {
+ public:
+  SearchResultTileItemView();
+  ~SearchResultTileItemView() override;
+
+  void SetSearchResult(SearchResult* item);
+
+  // Overridden from TileItemView:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+  // Overridden from SearchResultObserver:
+  void OnIconChanged() override;
+  void OnResultDestroying() override;
+
+ private:
+  // Owned by the model provided by the AppListViewDelegate.
+  SearchResult* item_;
+
+  DISALLOW_COPY_AND_ASSIGN(SearchResultTileItemView);
+};
+
+}  // namespace app_list
+
+#endif  // UI_APP_LIST_VIEWS_SEARCH_RESULT_TILE_ITEM_VIEW_H_
diff --git a/ui/app_list/views/start_page_view.cc b/ui/app_list/views/start_page_view.cc
index a13d8ad..dcf9c1d6 100644
--- a/ui/app_list/views/start_page_view.cc
+++ b/ui/app_list/views/start_page_view.cc
@@ -10,9 +10,11 @@
 #include "ui/app_list/app_list_model.h"
 #include "ui/app_list/app_list_view_delegate.h"
 #include "ui/app_list/search_result.h"
+#include "ui/app_list/views/all_apps_tile_item_view.h"
 #include "ui/app_list/views/app_list_main_view.h"
 #include "ui/app_list/views/search_box_view.h"
 #include "ui/app_list/views/search_result_list_view.h"
+#include "ui/app_list/views/search_result_tile_item_view.h"
 #include "ui/app_list/views/tile_item_view.h"
 #include "ui/gfx/canvas.h"
 #include "ui/views/background.h"
@@ -39,7 +41,8 @@
 const int kDummySearchBoxWidth = 480;
 
 // Tile container constants.
-const size_t kNumStartPageTiles = 5;
+const size_t kNumStartPageTiles = 4;
+const size_t kNumSearchResultTiles = 5;
 const int kTileSpacing = 10;
 
 // A placeholder search box which is sized to fit within the start page view.
@@ -131,11 +134,19 @@
   tiles_layout_manager->set_main_axis_alignment(
       views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
   tiles_container_->SetLayoutManager(tiles_layout_manager);
-  for (size_t i = 0; i < kNumStartPageTiles; ++i) {
-    TileItemView* tile_item = new TileItemView();
+
+  // Add SearchResultTileItemViews to the container.
+  for (size_t i = 0; i < std::max(kNumStartPageTiles, kNumSearchResultTiles);
+       ++i) {
+    SearchResultTileItemView* tile_item = new SearchResultTileItemView();
     tiles_container_->AddChildView(tile_item);
-    tile_views_.push_back(tile_item);
+    search_result_tile_views_.push_back(tile_item);
   }
+
+  // Also add a special "all apps" button to the end of the container.
+  all_apps_button_ =
+      new AllAppsTileItemView(app_list_main_view_->contents_view());
+  tiles_container_->AddChildView(all_apps_button_);
 }
 
 void StartPageView::SetModel(AppListModel* model) {
@@ -188,6 +199,10 @@
   Update();
 }
 
+TileItemView* StartPageView::all_apps_button() const {
+  return all_apps_button_;
+}
+
 bool StartPageView::OnKeyPressed(const ui::KeyEvent& event) {
   if (show_state_ == SHOW_SEARCH_RESULTS)
     return results_view_->OnKeyPressed(event);
@@ -209,16 +224,23 @@
 }
 
 void StartPageView::Update() {
+  size_t max_tiles = show_state_ == SHOW_START_PAGE ? kNumStartPageTiles
+                                                    : kNumSearchResultTiles;
   std::vector<SearchResult*> display_results =
-      AppListModel::FilterSearchResultsByDisplayType(search_results_model_,
-                                                     SearchResult::DISPLAY_TILE,
-                                                     kNumStartPageTiles);
-  for (size_t i = 0; i < kNumStartPageTiles; ++i) {
+      AppListModel::FilterSearchResultsByDisplayType(
+          search_results_model_, SearchResult::DISPLAY_TILE, max_tiles);
+
+  // Update the tile item results.
+  for (size_t i = 0; i < search_result_tile_views_.size(); ++i) {
     SearchResult* item = NULL;
     if (i < display_results.size())
       item = display_results[i];
-    tile_views_[i]->SetSearchResult(item);
+    search_result_tile_views_[i]->SetSearchResult(item);
   }
+
+  // Show or hide the all apps button (depending on the current show state).
+  all_apps_button_->SetVisible(show_state_ == SHOW_START_PAGE);
+
   tiles_container_->Layout();
   Layout();
   update_factory_.InvalidateWeakPtrs();
diff --git a/ui/app_list/views/start_page_view.h b/ui/app_list/views/start_page_view.h
index 958f2cc..ad6f8ca 100644
--- a/ui/app_list/views/start_page_view.h
+++ b/ui/app_list/views/start_page_view.h
@@ -16,9 +16,11 @@
 
 namespace app_list {
 
+class AllAppsTileItemView;
 class AppListMainView;
 class AppListViewDelegate;
 class SearchResultListView;
+class SearchResultTileItemView;
 class TileItemView;
 
 // The start page for the experimental app list.
@@ -37,7 +39,10 @@
 
   void UpdateForTesting();
 
-  const std::vector<TileItemView*>& tile_views() const { return tile_views_; }
+  const std::vector<SearchResultTileItemView*>& tile_views() const {
+    return search_result_tile_views_;
+  }
+  TileItemView* all_apps_button() const;
   SearchBoxView* dummy_search_box_view() { return search_box_view_; }
 
   // Overridden from views::View:
@@ -85,7 +90,8 @@
   views::View* instant_container_;  // Owned by views hierarchy.
   views::View* tiles_container_;    // Owned by views hierarchy.
 
-  std::vector<TileItemView*> tile_views_;
+  std::vector<SearchResultTileItemView*> search_result_tile_views_;
+  AllAppsTileItemView* all_apps_button_;
 
   ShowState show_state_;
 
diff --git a/ui/app_list/views/tile_item_view.cc b/ui/app_list/views/tile_item_view.cc
index 15889b19..a2018aa7 100644
--- a/ui/app_list/views/tile_item_view.cc
+++ b/ui/app_list/views/tile_item_view.cc
@@ -4,16 +4,8 @@
 
 #include "ui/app_list/views/tile_item_view.h"
 
-#include "base/strings/utf_string_conversions.h"
 #include "ui/app_list/app_list_constants.h"
-#include "ui/app_list/app_list_item.h"
-#include "ui/app_list/app_list_model.h"
-#include "ui/app_list/app_list_view_delegate.h"
-#include "ui/app_list/search_result.h"
 #include "ui/app_list/views/app_list_main_view.h"
-#include "ui/gfx/canvas.h"
-#include "ui/gfx/color_analysis.h"
-#include "ui/gfx/color_utils.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/controls/label.h"
@@ -30,7 +22,6 @@
 
 TileItemView::TileItemView()
     : views::CustomButton(this),
-      item_(NULL),
       icon_(new views::ImageView),
       title_(new views::Label) {
   views::BoxLayout* layout_manager = new views::BoxLayout(
@@ -58,51 +49,18 @@
 }
 
 TileItemView::~TileItemView() {
-  if (item_)
-    item_->RemoveObserver(this);
 }
 
-void TileItemView::SetSearchResult(SearchResult* item) {
-  SetVisible(item != NULL);
+void TileItemView::SetIcon(const gfx::ImageSkia& icon) {
+  icon_->SetImage(icon);
+}
 
-  SearchResult* old_item = item_;
-  if (old_item)
-    old_item->RemoveObserver(this);
-
-  item_ = item;
-
-  if (!item)
-    return;
-
-  item_->AddObserver(this);
-
-  title_->SetText(item_->title());
-
-  // Only refresh the icon if it's different from the old one. This prevents
-  // flickering.
-  if (old_item == NULL ||
-      !item->icon().BackedBySameObjectAs(old_item->icon())) {
-    OnIconChanged();
-  }
+void TileItemView::SetTitle(const base::string16& title) {
+  title_->SetText(title);
 }
 
 gfx::Size TileItemView::GetPreferredSize() const {
   return gfx::Size(kTileSize, kTileSize);
 }
 
-void TileItemView::ButtonPressed(views::Button* sender,
-                                 const ui::Event& event) {
-  item_->Open(event.flags());
-}
-
-void TileItemView::OnIconChanged() {
-  icon_->SetImage(item_->icon());
-}
-
-void TileItemView::OnResultDestroying() {
-  if (item_)
-    item_->RemoveObserver(this);
-  item_ = NULL;
-}
-
 }  // namespace app_list
diff --git a/ui/app_list/views/tile_item_view.h b/ui/app_list/views/tile_item_view.h
index 4853584..9140fa2 100644
--- a/ui/app_list/views/tile_item_view.h
+++ b/ui/app_list/views/tile_item_view.h
@@ -5,11 +5,15 @@
 #ifndef UI_APP_LIST_VIEWS_TILE_ITEM_VIEW_H_
 #define UI_APP_LIST_VIEWS_TILE_ITEM_VIEW_H_
 
+#include "base/strings/string16.h"
 #include "ui/app_list/app_list_export.h"
-#include "ui/app_list/search_result_observer.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/custom_button.h"
 
+namespace gfx {
+class ImageSkia;
+}
+
 namespace views {
 class ImageView;
 class Label;
@@ -17,32 +21,22 @@
 
 namespace app_list {
 
-class SearchResult;
-
 // The view for a tile in the app list on the start/search page.
 class APP_LIST_EXPORT TileItemView : public views::CustomButton,
-                                     public views::ButtonListener,
-                                     public SearchResultObserver {
+                                     public views::ButtonListener {
  public:
   TileItemView();
   ~TileItemView() override;
 
-  void SetSearchResult(SearchResult* item);
+ protected:
+  void SetIcon(const gfx::ImageSkia& icon);
+
+  void SetTitle(const base::string16& title);
 
  private:
   // Overridden from views::View:
   gfx::Size GetPreferredSize() const override;
 
-  // Overridden from views::ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
-  // Overridden from SearchResultObserver:
-  void OnIconChanged() override;
-  void OnResultDestroying() override;
-
-  // Owned by the model provided by the AppListViewDelegate.
-  SearchResult* item_;
-
   views::ImageView* icon_;  // Owned by views hierarchy.
   views::Label* title_;     // Owned by views hierarchy.
 
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 78c3361..7df7dbd 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -2175,6 +2175,9 @@
       <message name="IDS_APP_LIST_OPEN_FEEDBACK" desc="The menu entry to show the feedback UI.">
         Send feedback
       </message>
+      <message name="IDS_APP_LIST_ALL_APPS" desc="The text on the button that opens the app grid view in the app launcher.">
+        All apps
+      </message>
       <message name="IDS_APP_LIST_FOLDER_NAME_PLACEHOLDER" desc="The placeholder text for app list folder name.">
         Unnamed Folder
       </message>