diff options
author | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-04 03:11:26 +0000 |
---|---|---|
committer | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-04 03:11:26 +0000 |
commit | 919b2f83216c7a9f07d2e3569ad74ab8c1a06675 (patch) | |
tree | 7d190452fedf4dc798acfc32d2dd27afb241bf82 /chrome/browser | |
parent | d9440363af79abd7a4638b6949cd601449721551 (diff) | |
download | chromium_src-919b2f83216c7a9f07d2e3569ad74ab8c1a06675.zip chromium_src-919b2f83216c7a9f07d2e3569ad74ab8c1a06675.tar.gz chromium_src-919b2f83216c7a9f07d2e3569ad74ab8c1a06675.tar.bz2 |
app_list: Initial version of the start page.
- Add a StartPageService PKS that holds the start page contents and
app recommendation generator;
- Implement the initial app recommendation using the 4 most recent apps;
- Expose the start page contents in AppListViewDelegate;
- Update AppsGridView could to handle start page case;
- Implement a WebUI for the start page contents;
- Put the start page feature behind a flag;
- Add a switch to override the start page url for easier mocking;
BUG=268660,268661
Review URL: https://codereview.chromium.org/25152002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@226940 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
27 files changed, 1025 insertions, 1 deletions
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index ee4db2e..5ab1999 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -1707,6 +1707,15 @@ const Experiment kExperiments[] = { kOsMac | kOsWin, SINGLE_VALUE_TYPE(switches::kDisableAppList) }, +#if defined(ENABLE_APP_LIST) + { + "enable-app-launcher-start-page", + IDS_FLAGS_ENABLE_APP_LIST_START_PAGE_NAME, + IDS_FLAGS_ENABLE_APP_LIST_START_PAGE_DESCRIPTION, + kOsWin | kOsCrOS, + SINGLE_VALUE_TYPE(switches::kShowAppListStartPage) + }, +#endif #if defined(OS_CHROMEOS) { "disable-user-image-sync", diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 0435551..43eaafe 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd @@ -24,6 +24,11 @@ <structure name="IDR_ABOUT_WELCOME_HTML" file="resources\about_welcome_android\about_welcome_android.html" flattenhtml="true" type="chrome_html" /> <structure name="IDR_ABOUT_WELCOME_CSS" file="resources\about_welcome_android\about_welcome_android.css" flattenhtml="true" type="chrome_html" /> </if> + <if expr="pp_ifdef('enable_app_list')"> + <structure name="IDR_APP_LIST_START_PAGE_CSS" file="resources\app_list\start_page.css" flattenhtml="true" type="chrome_html" /> + <structure name="IDR_APP_LIST_START_PAGE_HTML" file="resources\app_list\start_page.html" flattenhtml="true" type="chrome_html" /> + <structure name="IDR_APP_LIST_START_PAGE_JS" file="resources\app_list\start_page.js" flattenhtml="true" type="chrome_html" /> + </if> <if expr="pp_ifdef('chromeos')"> <structure name="IDR_DEMO_USER_LOGIN_HTML" file="resources\chromeos\login\demo_user_login.html" flattenhtml="true" type="chrome_html" /> <structure name="IDR_DEMO_USER_LOGIN_JS" file="resources\chromeos\login\demo_user_login.js" flattenhtml="true" type="chrome_html" /> diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc index e70f78e..87202ab 100644 --- a/chrome/browser/chromeos/login/login_utils.cc +++ b/chrome/browser/chromeos/login/login_utils.cc @@ -58,6 +58,7 @@ #include "chrome/browser/signin/token_service_factory.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/profile_sync_service_factory.h" +#include "chrome/browser/ui/app_list/start_page_service.h" #include "chrome/browser/ui/startup/startup_browser_creator.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" @@ -296,6 +297,9 @@ void LoginUtilsImpl::DoBrowserLaunch(Profile* profile, first_run, &return_code); + // Triggers app launcher start page service to load start page web contents. + app_list::StartPageService::Get(profile); + // Mark login host for deletion after browser starts. This // guarantees that the message loop will be referenced by the // browser before it is dereferenced by the login host. diff --git a/chrome/browser/extensions/api/app_runtime/app_runtime_api.cc b/chrome/browser/extensions/api/app_runtime/app_runtime_api.cc index ab955db..a0725b7 100644 --- a/chrome/browser/extensions/api/app_runtime/app_runtime_api.cc +++ b/chrome/browser/extensions/api/app_runtime/app_runtime_api.cc @@ -8,9 +8,12 @@ #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" #include "base/values.h" #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h" #include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/api/app_runtime.h" @@ -46,6 +49,8 @@ void DispatchOnLaunchedEventImpl(const std::string& extension_id, event->restrict_to_profile = profile; system->event_router()->DispatchEventWithLazyListener(extension_id, event.Pass()); + system->extension_service()->extension_prefs()->SetLastLaunchTime( + extension_id, base::Time::Now()); } } // anonymous namespace diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc index 3c66561..a9d5d0e 100644 --- a/chrome/browser/extensions/extension_prefs.cc +++ b/chrome/browser/extensions/extension_prefs.cc @@ -184,6 +184,9 @@ const char kPrefWasInstalledByDefault[] = "was_installed_by_default"; // Key for Geometry Cache preference. const char kPrefGeometryCache[] = "geometry_cache"; +// A preference that indicates when an extension is last launched. +const char kPrefLastLaunchTime[] = "last_launch_time"; + // Provider of write access to a dictionary storing extension prefs. class ScopedExtensionPrefUpdate : public DictionaryPrefUpdate { public: @@ -1549,6 +1552,28 @@ base::Time ExtensionPrefs::GetInstallTime( return base::Time::FromInternalValue(install_time_i64); } +base::Time ExtensionPrefs::GetLastLaunchTime( + const std::string& extension_id) const { + const DictionaryValue* extension = GetExtensionPref(extension_id); + if (!extension) + return base::Time(); + + std::string launch_time_str; + if (!extension->GetString(kPrefLastLaunchTime, &launch_time_str)) + return base::Time(); + int64 launch_time_i64 = 0; + if (!base::StringToInt64(launch_time_str, &launch_time_i64)) + return base::Time(); + return base::Time::FromInternalValue(launch_time_i64); +} + +void ExtensionPrefs::SetLastLaunchTime(const std::string& extension_id, + const base::Time& time) { + DCHECK(Extension::IdIsValid(extension_id)); + ScopedExtensionPrefUpdate update(prefs_, extension_id); + SaveTime(update.Get(), kPrefLastLaunchTime, time); +} + void ExtensionPrefs::GetExtensions(ExtensionIdList* out) { CHECK(out); diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h index 1a03127..c8e3c48 100644 --- a/chrome/browser/extensions/extension_prefs.h +++ b/chrome/browser/extensions/extension_prefs.h @@ -464,6 +464,11 @@ class ExtensionPrefs : public ExtensionScopedPrefs, // found. base::Time GetInstallTime(const std::string& extension_id) const; + // Gets/sets the last launch time of an extension. + base::Time GetLastLaunchTime(const std::string& extension_id) const; + void SetLastLaunchTime(const std::string& extension_id, + const base::Time& time); + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); bool extensions_disabled() { return extensions_disabled_; } diff --git a/chrome/browser/extensions/install_observer.h b/chrome/browser/extensions/install_observer.h index 95370da..cc79df0 100644 --- a/chrome/browser/extensions/install_observer.h +++ b/chrome/browser/extensions/install_observer.h @@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_EXTENSIONS_INSTALL_OBSERVER_H_ #define CHROME_BROWSER_EXTENSIONS_INSTALL_OBSERVER_H_ +#include <string> + namespace gfx { class ImageSkia; } diff --git a/chrome/browser/resources/app_list/recommended_apps.css b/chrome/browser/resources/app_list/recommended_apps.css new file mode 100644 index 0000000..b02c496 --- /dev/null +++ b/chrome/browser/resources/app_list/recommended_apps.css @@ -0,0 +1,31 @@ +/* Copyright 2013 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. */ + +.recommended-apps { + -webkit-align-items: center; + -webkit-justify-content: space-around; + display: -webkit-flex; + padding: 0 10px; +} + +.app { + background-position: center 5px; + background-repeat: no-repeat; + background-size: 48px; + color: rgb(90, 90, 90); + cursor: default; + font-size: 11px; + font-weight: bold; + height: 20px; + overflow: hidden; + padding-top: 58px; + text-align: center; + text-overflow: ellipsis; + white-space: nowrap; + width: 88px; +} + +.app:hover { + background-color: rgb(230, 230, 230); +} diff --git a/chrome/browser/resources/app_list/recommended_apps.js b/chrome/browser/resources/app_list/recommended_apps.js new file mode 100644 index 0000000..d5fe706 --- /dev/null +++ b/chrome/browser/resources/app_list/recommended_apps.js @@ -0,0 +1,90 @@ +// Copyright 2013 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. + +/** + * @fileoverview Implement the recommended apps card in the launcher start page. + */ + +cr.define('appList.startPage', function() { + 'use strict'; + + /** + * Create a view with icon and label for the given app data. + * @constructor + * @extends {HTMLDivElement} + */ + var AppItemView = cr.ui.define('div'); + + AppItemView.prototype = { + __proto__: HTMLDivElement.prototype, + + /** + * The app id of the app displayed by this view. Used to launch + * the app when the view is clicked. + * @type {string} + */ + appId: '', + + /** + * Sets the icon URL to display the app icon. + * @type {string} + */ + set iconUrl(url) { + this.style.backgroundImage = 'url(' + url + ')'; + }, + + /** + * Sets the text title. + * @type {string} + */ + set textTitle(title) { + this.textContent = title; + }, + + /** @override */ + decorate: function() { + this.className = 'app'; + this.addEventListener('click', this.handleClick_.bind(this)); + }, + + /** + * Handles 'click' event. + * @private + */ + handleClick_: function() { + assert(this.appId); + chrome.send('launchApp', [this.appId]); + } + }; + + /** + * Create recommended apps card. + * @constructor + * @extends {HTMLDivElement} + */ + var RecommendedApps = cr.ui.define('div'); + + RecommendedApps.prototype = { + __proto__: HTMLDivElement.prototype, + + /** @override */ + decorate: function() { + this.className = 'recommended-apps'; + }, + + /** + * Sets the apps to be displayed in this card. + */ + setApps: function(apps) { + this.textContent = ''; + for (var i = 0; i < apps.length; ++i) { + this.appendChild(new AppItemView(apps[i])); + } + } + }; + + return { + RecommendedApps: RecommendedApps + }; +}); diff --git a/chrome/browser/resources/app_list/start_page.css b/chrome/browser/resources/app_list/start_page.css new file mode 100644 index 0000000..cd8f3c0 --- /dev/null +++ b/chrome/browser/resources/app_list/start_page.css @@ -0,0 +1,26 @@ +/* Copyright 2013 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. */ + +html, +body, +#start-page { + background-color: rgb(245, 245, 245); + height: 100%; + margin: 0; + overflow: hidden; + padding: 0; + width: 100%; +} + +#start-page { + -webkit-align-items: stretch; + -webkit-flex-direction: column; + -webkit-justify-content: flex-start; + -webkit-user-select: none; + box-sizing: border-box; + display: -webkit-flex; + padding: 10px; +} + +<include src="recommended_apps.css"/> diff --git a/chrome/browser/resources/app_list/start_page.html b/chrome/browser/resources/app_list/start_page.html new file mode 100644 index 0000000..8b86ae1 --- /dev/null +++ b/chrome/browser/resources/app_list/start_page.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<html i18n-values="dir:textdirection"> +<head> + <meta charset="utf-8"> + <link rel="stylesheet" href="chrome://app-list/start_page.css"> + <script src="chrome://resources/js/load_time_data.js"></script> + <script src="chrome://resources/js/cr.js"></script> + <script src="chrome://resources/js/cr/ui.js"></script> + <script src="chrome://resources/js/util.js"></script> + <script src="chrome://app-list/strings.js"></script> + <script src="chrome://app-list/start_page.js"></script> +</head> + +<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize"> + <div id="start-page"></div> + <script src="chrome://resources/js/i18n_template2.js"></script> +</body> +</html> diff --git a/chrome/browser/resources/app_list/start_page.js b/chrome/browser/resources/app_list/start_page.js new file mode 100644 index 0000000..942b3a9 --- /dev/null +++ b/chrome/browser/resources/app_list/start_page.js @@ -0,0 +1,72 @@ +// Copyright 2013 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. + +/** + * @fileoverview App launcher start page implementation. + */ + +<include src="recommended_apps.js"/> + +cr.define('appList.startPage', function() { + 'use strict'; + + /** + * Creates a StartPage object. + * @constructor + * @extends {HTMLDivElement} + */ + var StartPage = cr.ui.define('div'); + + StartPage.prototype = { + __proto__: HTMLDivElement.prototype, + + /** + * Instance of the recommended apps card. + * @type {appsList.startPage.RecommendedApps} + * @private + */ + recommendedApps_: null, + + /** @override */ + decorate: function() { + this.recommendedApps_ = new appList.startPage.RecommendedApps(); + this.appendChild(this.recommendedApps_); + }, + + /** + * Sets the recommended apps. + * @param {!Array.<!{appId: string, + * iconUrl: string, + * textTitle: string}>} apps An array of app info + * dictionary to be displayed in the AppItemView. + */ + setRecommendedApps: function(apps) { + this.recommendedApps_.setApps(apps); + } + }; + + /** + * Initialize the page. + */ + function initialize() { + StartPage.decorate($('start-page')); + chrome.send('initialize'); + } + + /** + * Sets the recommended apps. + * @param {Array.<Object>} apps An array of app info dictionary. + */ + function setRecommendedApps(apps) { + $('start-page').setRecommendedApps(apps); + } + + return { + initialize: initialize, + setRecommendedApps: setRecommendedApps + }; +}); + +document.addEventListener('contextmenu', function(e) { e.preventDefault(); }); +document.addEventListener('DOMContentLoaded', appList.startPage.initialize); diff --git a/chrome/browser/ui/app_list/app_list_service.h b/chrome/browser/ui/app_list/app_list_service.h index 60144f0..ddbd76a 100644 --- a/chrome/browser/ui/app_list/app_list_service.h +++ b/chrome/browser/ui/app_list/app_list_service.h @@ -77,7 +77,7 @@ class AppListService { // Get the window the app list is in, or NULL if the app list isn't visible. virtual gfx::NativeWindow GetAppListWindow() = 0; - // Exposed to allow testing of the controller delegate. + // Creates a platform specific AppListControllerDelegate. virtual AppListControllerDelegate* CreateControllerDelegate() = 0; protected: diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.cc b/chrome/browser/ui/app_list/app_list_view_delegate.cc index 57b0253..8321882 100644 --- a/chrome/browser/ui/app_list/app_list_view_delegate.cc +++ b/chrome/browser/ui/app_list/app_list_view_delegate.cc @@ -19,6 +19,7 @@ #include "chrome/browser/ui/app_list/apps_model_builder.h" #include "chrome/browser/ui/app_list/chrome_app_list_item.h" #include "chrome/browser/ui/app_list/search/search_controller.h" +#include "chrome/browser/ui/app_list/start_page_service.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/host_desktop.h" @@ -291,3 +292,12 @@ void AppListViewDelegate::OnProfileNameChanged( const base::string16& old_profile_name) { OnProfileChanged(); } + +content::WebContents* AppListViewDelegate::GetStartPageContents() { + app_list::StartPageService* service = + app_list::StartPageService::Get(profile_); + if (!service) + return NULL; + + return service->contents(); +} diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.h b/chrome/browser/ui/app_list/app_list_view_delegate.h index c1f3a95..2a43cd4 100644 --- a/chrome/browser/ui/app_list/app_list_view_delegate.h +++ b/chrome/browser/ui/app_list/app_list_view_delegate.h @@ -80,6 +80,7 @@ class AppListViewDelegate : public app_list::AppListViewDelegate, virtual void OpenFeedback() OVERRIDE; virtual void ShowForProfileByPath( const base::FilePath& profile_path) OVERRIDE; + virtual content::WebContents* GetStartPageContents() OVERRIDE; // Overridden from content::NotificationObserver: virtual void Observe(int type, diff --git a/chrome/browser/ui/app_list/recommended_apps.cc b/chrome/browser/ui/app_list/recommended_apps.cc new file mode 100644 index 0000000..5b32f10 --- /dev/null +++ b/chrome/browser/ui/app_list/recommended_apps.cc @@ -0,0 +1,139 @@ +// Copyright 2013 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/app_list/recommended_apps.h" + +#include <algorithm> +#include <vector> + +#include "base/bind.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system_factory.h" +#include "chrome/browser/extensions/install_tracker.h" +#include "chrome/browser/extensions/install_tracker_factory.h" +#include "chrome/browser/ui/app_list/recommended_apps_observer.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/pref_names.h" + +namespace app_list { + +namespace { + +struct AppSortInfo { + AppSortInfo() : app(NULL) {} + AppSortInfo(const extensions::Extension* app, + const base::Time& last_launch_time) + : app(app), last_launch_time(last_launch_time) {} + + const extensions::Extension* app; + base::Time last_launch_time; +}; + +bool AppLaunchedMoreRecent(const AppSortInfo& app1, const AppSortInfo& app2) { + return app1.last_launch_time > app2.last_launch_time; +} + +} // namespace + +RecommendedApps::RecommendedApps(Profile* profile) : profile_(profile) { + extensions::InstallTrackerFactory::GetForProfile(profile_)->AddObserver(this); + + ExtensionService* service = + extensions::ExtensionSystem::Get(profile_)->extension_service(); + extensions::ExtensionPrefs* prefs = service->extension_prefs(); + pref_change_registrar_.Init(prefs->pref_service()); + pref_change_registrar_.Add(prefs::kExtensionsPref, + base::Bind(&RecommendedApps::Update, + base::Unretained(this))); + + Update(); +} + +RecommendedApps::~RecommendedApps() { + extensions::InstallTrackerFactory::GetForProfile(profile_) + ->RemoveObserver(this); +} + +void RecommendedApps::AddObserver(RecommendedAppsObserver* observer) { + observers_.AddObserver(observer); +} + +void RecommendedApps::RemoveObserver(RecommendedAppsObserver* observer) { + observers_.RemoveObserver(observer); +} + +void RecommendedApps::Update() { + ExtensionService* service = + extensions::ExtensionSystem::Get(profile_)->extension_service(); + extensions::ExtensionPrefs* prefs = service->extension_prefs(); + + std::vector<AppSortInfo> sorted_apps; + const ExtensionSet* extensions = service->extensions(); + for (ExtensionSet::const_iterator app = extensions->begin(); + app != extensions->end(); ++app) { + if (!(*app)->ShouldDisplayInAppLauncher()) + continue; + + sorted_apps.push_back( + AppSortInfo(app->get(), prefs->GetLastLaunchTime((*app)->id()))); + } + + std::sort(sorted_apps.begin(), sorted_apps.end(), &AppLaunchedMoreRecent); + + const size_t kMaxRecommendedApps = 4; + sorted_apps.resize(std::min(kMaxRecommendedApps, sorted_apps.size())); + + Apps new_recommends; + for (size_t i = 0; i < sorted_apps.size(); ++i) + new_recommends.push_back(sorted_apps[i].app); + + const bool changed = apps_.size() != new_recommends.size() || + !std::equal(apps_.begin(), apps_.end(), new_recommends.begin()); + if (changed) { + apps_.swap(new_recommends); + FOR_EACH_OBSERVER( + RecommendedAppsObserver, observers_, OnRecommendedAppsChanged()); + } +} + +void RecommendedApps::OnBeginExtensionInstall(const std::string& extension_id, + const std::string& extension_name, + const gfx::ImageSkia& installing_icon, + bool is_app, + bool is_platform_app) {} + +void RecommendedApps::OnDownloadProgress(const std::string& extension_id, + int percent_downloaded) {} + +void RecommendedApps::OnInstallFailure(const std::string& extension_id) {} + +void RecommendedApps::OnExtensionInstalled( + const extensions::Extension* extension) { + Update(); +} + +void RecommendedApps::OnExtensionLoaded( + const extensions::Extension* extension) { + Update(); +} + +void RecommendedApps::OnExtensionUnloaded( + const extensions::Extension* extension) { + Update(); +} + +void RecommendedApps::OnExtensionUninstalled( + const extensions::Extension* extension) { + Update(); +} + +void RecommendedApps::OnAppsReordered() {} + +void RecommendedApps::OnAppInstalledToAppList( + const std::string& extension_id) {} + +void RecommendedApps::OnShutdown() {} + +} // namespace app_list diff --git a/chrome/browser/ui/app_list/recommended_apps.h b/chrome/browser/ui/app_list/recommended_apps.h new file mode 100644 index 0000000..c89f39a --- /dev/null +++ b/chrome/browser/ui/app_list/recommended_apps.h @@ -0,0 +1,72 @@ +// Copyright 2013 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_APP_LIST_RECOMMENDED_APPS_H_ +#define CHROME_BROWSER_UI_APP_LIST_RECOMMENDED_APPS_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/observer_list.h" +#include "base/prefs/pref_change_registrar.h" +#include "chrome/browser/extensions/install_observer.h" + +class Profile; + +namespace app_list { + +class RecommendedAppsObserver; + +// A class that maintains a list of recommended apps by watching changes +// to app state. +class RecommendedApps : public extensions::InstallObserver { + public: + typedef std::vector<scoped_refptr<const extensions::Extension> > Apps; + + explicit RecommendedApps(Profile* profile); + virtual ~RecommendedApps(); + + void AddObserver(RecommendedAppsObserver* observer); + void RemoveObserver(RecommendedAppsObserver* observer); + + const Apps& apps() const { return apps_; } + + private: + void Update(); + + // extensions::InstallObserver overrides: + virtual void OnBeginExtensionInstall(const std::string& extension_id, + const std::string& extension_name, + const gfx::ImageSkia& installing_icon, + bool is_app, + bool is_platform_app) OVERRIDE; + virtual void OnDownloadProgress(const std::string& extension_id, + int percent_downloaded) OVERRIDE; + virtual void OnInstallFailure(const std::string& extension_id) OVERRIDE; + virtual void OnExtensionInstalled( + const extensions::Extension* extension) OVERRIDE; + virtual void OnExtensionLoaded( + const extensions::Extension* extension) OVERRIDE; + virtual void OnExtensionUnloaded( + const extensions::Extension* extension) OVERRIDE; + virtual void OnExtensionUninstalled( + const extensions::Extension* extension) OVERRIDE; + virtual void OnAppsReordered() OVERRIDE; + virtual void OnAppInstalledToAppList( + const std::string& extension_id) OVERRIDE; + virtual void OnShutdown() OVERRIDE; + + Profile* profile_; + PrefChangeRegistrar pref_change_registrar_; + + Apps apps_; + ObserverList<RecommendedAppsObserver, true> observers_; + + DISALLOW_COPY_AND_ASSIGN(RecommendedApps); +}; + +} // namespace app_list + +#endif // CHROME_BROWSER_UI_APP_LIST_RECOMMENDED_APPS_H_ diff --git a/chrome/browser/ui/app_list/recommended_apps_observer.h b/chrome/browser/ui/app_list/recommended_apps_observer.h new file mode 100644 index 0000000..ab8e76a --- /dev/null +++ b/chrome/browser/ui/app_list/recommended_apps_observer.h @@ -0,0 +1,22 @@ +// Copyright 2013 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_APP_LIST_RECOMMENDED_APPS_OBSERVER_H_ +#define CHROME_BROWSER_UI_APP_LIST_RECOMMENDED_APPS_OBSERVER_H_ + +namespace app_list { + +// An interface for observing RecommendedApps change. +class RecommendedAppsObserver { + public: + // Invoked when RecommendedApps changed. + virtual void OnRecommendedAppsChanged() = 0; + + protected: + virtual ~RecommendedAppsObserver() {} +}; + +} // namespace app_list + +#endif // CHROME_BROWSER_UI_APP_LIST_RECOMMENDED_APPS_OBSERVER_H_ diff --git a/chrome/browser/ui/app_list/start_page_service.cc b/chrome/browser/ui/app_list/start_page_service.cc new file mode 100644 index 0000000..6c9d35f --- /dev/null +++ b/chrome/browser/ui/app_list/start_page_service.cc @@ -0,0 +1,123 @@ +// Copyright 2013 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/app_list/start_page_service.h" + +#include <string> + +#include "base/command_line.h" +#include "base/memory/singleton.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/extensions/extension_system_factory.h" +#include "chrome/browser/extensions/install_tracker_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/app_list/recommended_apps.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/url_constants.h" +#include "components/browser_context_keyed_service/browser_context_dependency_manager.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/web_contents.h" + +namespace app_list { + +class StartPageService::Factory : public BrowserContextKeyedServiceFactory { + public: + static StartPageService* GetForProfile(Profile* profile) { + if (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kShowAppListStartPage)) { + return NULL; + } + + return static_cast<StartPageService*>( + GetInstance()->GetServiceForBrowserContext(profile, true)); + } + + static Factory* GetInstance() { + return Singleton<Factory>::get(); + } + + private: + friend struct DefaultSingletonTraits<Factory>; + + Factory() + : BrowserContextKeyedServiceFactory( + "AppListStartPageService", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(extensions::ExtensionSystemFactory::GetInstance()); + DependsOn(extensions::InstallTrackerFactory::GetInstance()); + } + + virtual ~Factory() {} + + // BrowserContextKeyedServiceFactory overrides: + virtual BrowserContextKeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const OVERRIDE { + Profile* profile = static_cast<Profile*>(context); + return new StartPageService(profile); + } + + DISALLOW_COPY_AND_ASSIGN(Factory); +}; + +class StartPageService::ExitObserver : public content::NotificationObserver { + public: + explicit ExitObserver(StartPageService* service) : service_(service) { + registrar_.Add(this, + chrome::NOTIFICATION_APP_TERMINATING, + content::NotificationService::AllSources()); + } + virtual ~ExitObserver() {} + + private: + // content::NotificationObserver + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE { + DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type); + service_->Shutdown(); + } + + StartPageService* service_; // Owner of this class. + content::NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(ExitObserver); +}; + +// static +StartPageService* StartPageService::Get(Profile* profile) { + return Factory::GetForProfile(profile); +} + +StartPageService::StartPageService(Profile* profile) + : profile_(profile), + exit_observer_(new ExitObserver(this)), + recommended_apps_(new RecommendedApps(profile)) { + contents_.reset(content::WebContents::Create( + content::WebContents::CreateParams(profile_))); + + GURL url(chrome::kChromeUIAppListStartPageURL); + CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kAppListStartPageURL)) { + url = GURL( + command_line->GetSwitchValueASCII(switches::kAppListStartPageURL)); + } + + contents_->GetController().LoadURL( + url, + content::Referrer(), + content::PAGE_TRANSITION_AUTO_TOPLEVEL, + std::string()); +} + +StartPageService::~StartPageService() {} + +void StartPageService::Shutdown() { + contents_.reset(); +} + +} // namespace app_list diff --git a/chrome/browser/ui/app_list/start_page_service.h b/chrome/browser/ui/app_list/start_page_service.h new file mode 100644 index 0000000..1b7db3b --- /dev/null +++ b/chrome/browser/ui/app_list/start_page_service.h @@ -0,0 +1,63 @@ +// Copyright 2013 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_APP_LIST_START_PAGE_SERVICE_H_ +#define CHROME_BROWSER_UI_APP_LIST_START_PAGE_SERVICE_H_ + +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "components/browser_context_keyed_service/browser_context_keyed_service.h" +#include "content/public/browser/web_contents.h" + +namespace extensions { +class Extension; +} + +class Profile; + +namespace app_list { + +class RecommendedApps; + +// StartPageService collects data to be displayed in app list's start page +// and hosts the start page contents. +class StartPageService : public BrowserContextKeyedService { + public: + typedef std::vector<scoped_refptr<const extensions::Extension> > + ExtensionList; + // Gets the instance for the given profile. + static StartPageService* Get(Profile* profile); + + content::WebContents* contents() { return contents_.get(); } + RecommendedApps* recommended_apps() { return recommended_apps_.get(); } + + private: + // A BrowserContextKeyedServiceFactory for this service. + class Factory; + + // ExitObserver to shutdown the service on exiting. WebContents depends + // on the profile and needs to be closed before the profile and its + // keyed service shutdown. + class ExitObserver; + + explicit StartPageService(Profile* profile); + virtual ~StartPageService(); + + // BrowserContextKeyedService overrides: + virtual void Shutdown() OVERRIDE; + + Profile* profile_; + scoped_ptr<content::WebContents> contents_; + scoped_ptr<ExitObserver> exit_observer_; + scoped_ptr<RecommendedApps> recommended_apps_; + + DISALLOW_COPY_AND_ASSIGN(StartPageService); +}; + +} // namespace app_list + +#endif // CHROME_BROWSER_UI_APP_LIST_START_PAGE_SERVICE_H_ diff --git a/chrome/browser/ui/extensions/application_launch.cc b/chrome/browser/ui/extensions/application_launch.cc index 92c25e7..8e96a5e 100644 --- a/chrome/browser/ui/extensions/application_launch.cc +++ b/chrome/browser/ui/extensions/application_launch.cc @@ -335,6 +335,7 @@ WebContents* OpenEnabledApplication(const AppLaunchParams& params) { ExtensionPrefs* prefs = extensions::ExtensionSystem::Get(profile)-> extension_service()->extension_prefs(); prefs->SetActiveBit(extension->id(), true); + prefs->SetLastLaunchTime(extension->id(), base::Time::Now()); UMA_HISTOGRAM_ENUMERATION("Extensions.AppLaunchContainer", container, 100); diff --git a/chrome/browser/ui/webui/app_list/start_page_browsertest.js b/chrome/browser/ui/webui/app_list/start_page_browsertest.js new file mode 100644 index 0000000..1fa27db --- /dev/null +++ b/chrome/browser/ui/webui/app_list/start_page_browsertest.js @@ -0,0 +1,65 @@ +// Copyright 2013 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. + +/** + * TestFixture for kiosk app settings WebUI testing. + * @extends {testing.Test} + * @constructor + **/ +function AppListStartPageWebUITest() {} + +AppListStartPageWebUITest.prototype = { + __proto__: testing.Test.prototype, + + /** + * Browser to app launcher start page. + */ + browsePreload: 'chrome://app-list/', + + /** + * Recommend apps data. + * @private + */ + recommendedApps_: [ + { + 'appId': 'app_id_1', + 'textTitle': 'app 1', + 'iconUrl': 'icon_url_1' + }, + { + 'appId': 'app_id_2', + 'textTitle': 'app 2', + 'iconUrl': 'icon_url_2' + }, + ], + + /** @override */ + preLoad: function() { + this.makeAndRegisterMockHandler(['initialize', 'launchApp']); + this.mockHandler.stubs().initialize().will(callFunction(function() { + appList.startPage.setRecommendedApps(this.recommendedApps_); + }.bind(this))); + this.mockHandler.stubs().launchApp(ANYTHING); + } +}; + +TEST_F('AppListStartPageWebUITest', 'Basic', function() { + assertEquals(this.browsePreload, document.location.href); + + var recommendedApp = $('start-page').querySelector('.recommended-apps'); + assertEquals(this.recommendedApps_.length, recommendedApp.childElementCount); + for (var i = 0; i < recommendedApp.childElementCount; ++i) { + assertEquals(this.recommendedApps_[i].appId, + recommendedApp.children[i].appId); + } +}); + +TEST_F('AppListStartPageWebUITest', 'ClickToLaunch', function() { + var recommendedApp = $('start-page').querySelector('.recommended-apps'); + for (var i = 0; i < recommendedApp.childElementCount; ++i) { + this.mockHandler.expects(once()).launchApp( + [this.recommendedApps_[i].appId]); + cr.dispatchSimpleEvent(recommendedApp.children[i], 'click'); + } +}); diff --git a/chrome/browser/ui/webui/app_list/start_page_handler.cc b/chrome/browser/ui/webui/app_list/start_page_handler.cc new file mode 100644 index 0000000..a7be1ee --- /dev/null +++ b/chrome/browser/ui/webui/app_list/start_page_handler.cc @@ -0,0 +1,112 @@ +// Copyright 2013 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/webui/app_list/start_page_handler.h" + +#include <string> + +#include "base/bind.h" +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/app_list/app_list_controller_delegate.h" +#include "chrome/browser/ui/app_list/app_list_service.h" +#include "chrome/browser/ui/app_list/recommended_apps.h" +#include "chrome/browser/ui/app_list/start_page_service.h" +#include "chrome/browser/ui/webui/extensions/extension_icon_source.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_icon_set.h" +#include "content/public/browser/web_ui.h" +#include "ui/events/event_constants.h" + +namespace app_list { + +namespace { + +scoped_ptr<base::DictionaryValue> CreateAppInfo( + const extensions::Extension* app) { + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue); + dict->SetString("appId", app->id()); + dict->SetString("textTitle", app->short_name()); + dict->SetString("title", app->name()); + + const bool grayscale = false; + bool icon_exists = true; + GURL icon_url = extensions::ExtensionIconSource::GetIconURL( + app, + extension_misc::EXTENSION_ICON_MEDIUM, + ExtensionIconSet::MATCH_BIGGER, + grayscale, + &icon_exists); + dict->SetString("iconUrl", icon_url.spec()); + + return dict.Pass(); +} + +} // namespace + +StartPageHandler::StartPageHandler() : recommended_apps_(NULL) {} + +StartPageHandler::~StartPageHandler() { + if (recommended_apps_) + recommended_apps_->RemoveObserver(this); +} + +void StartPageHandler::RegisterMessages() { + web_ui()->RegisterMessageCallback( + "initialize", + base::Bind(&StartPageHandler::HandleInitialize, base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "launchApp", + base::Bind(&StartPageHandler::HandleLaunchApp, base::Unretained(this))); +} + +void StartPageHandler::OnRecommendedAppsChanged() { + SendRecommendedApps(); +} + +void StartPageHandler::SendRecommendedApps() { + const RecommendedApps::Apps& recommends = recommended_apps_->apps(); + + base::ListValue recommended_list; + for (size_t i = 0; i < recommends.size(); ++i) { + recommended_list.Append(CreateAppInfo(recommends[i].get()).release()); + } + + web_ui()->CallJavascriptFunction("appList.startPage.setRecommendedApps", + recommended_list); +} + +void StartPageHandler::HandleInitialize(const base::ListValue* args) { + Profile* profile = Profile::FromWebUI(web_ui()); + recommended_apps_ = StartPageService::Get(profile)->recommended_apps(); + recommended_apps_->AddObserver(this); + + SendRecommendedApps(); +} + +void StartPageHandler::HandleLaunchApp(const base::ListValue* args) { + std::string app_id; + CHECK(args->GetString(0, &app_id)); + + Profile* profile = Profile::FromWebUI(web_ui()); + ExtensionService* service = + extensions::ExtensionSystem::Get(profile)->extension_service(); + const extensions::Extension* app = service->GetInstalledExtension(app_id); + if (!app) { + NOTREACHED(); + return; + } + + scoped_ptr<AppListControllerDelegate> controller( + AppListService::Get()->CreateControllerDelegate()); + controller->ActivateApp(profile, + app, + AppListControllerDelegate::LAUNCH_FROM_APP_LIST, + ui::EF_NONE); +} + +} // namespace app_list diff --git a/chrome/browser/ui/webui/app_list/start_page_handler.h b/chrome/browser/ui/webui/app_list/start_page_handler.h new file mode 100644 index 0000000..54e79fe --- /dev/null +++ b/chrome/browser/ui/webui/app_list/start_page_handler.h @@ -0,0 +1,49 @@ +// Copyright 2013 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_WEBUI_APP_LIST_START_PAGE_HANDLER_H_ +#define CHROME_BROWSER_UI_WEBUI_APP_LIST_START_PAGE_HANDLER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "chrome/browser/ui/app_list/recommended_apps_observer.h" +#include "content/public/browser/web_ui_message_handler.h" + +namespace base { +class ListValue; +} + +namespace app_list { + +class RecommendedApps; + +// Handler for the app launcher start page. +class StartPageHandler : public content::WebUIMessageHandler, + public RecommendedAppsObserver { + public: + StartPageHandler(); + virtual ~StartPageHandler(); + + private: + // content::WebUIMessageHandler overrides: + virtual void RegisterMessages() OVERRIDE; + + // RecommendedAppsObserver overrdies: + virtual void OnRecommendedAppsChanged() OVERRIDE; + + // Creates a ListValue for the recommended apps and sends it to js side. + void SendRecommendedApps(); + + // JS callbacks. + void HandleInitialize(const base::ListValue* args); + void HandleLaunchApp(const base::ListValue* args); + + RecommendedApps* recommended_apps_; // Not owned. + + DISALLOW_COPY_AND_ASSIGN(StartPageHandler); +}; + +} // namespace app_list + +#endif // CHROME_BROWSER_UI_WEBUI_APP_LIST_START_PAGE_HANDLER_H_ diff --git a/chrome/browser/ui/webui/app_list/start_page_ui.cc b/chrome/browser/ui/webui/app_list/start_page_ui.cc new file mode 100644 index 0000000..5a88ec3 --- /dev/null +++ b/chrome/browser/ui/webui/app_list/start_page_ui.cc @@ -0,0 +1,39 @@ +// Copyright 2013 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/webui/app_list/start_page_ui.h" + +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/webui/app_list/start_page_handler.h" +#include "chrome/common/url_constants.h" +#include "content/public/browser/web_ui.h" +#include "content/public/browser/web_ui_data_source.h" +#include "grit/browser_resources.h" + +namespace app_list { + +StartPageUI::StartPageUI(content::WebUI* web_ui) + : content::WebUIController(web_ui) { + web_ui->AddMessageHandler(new StartPageHandler); + InitDataSource(); +} + +StartPageUI::~StartPageUI() {} + +void StartPageUI::InitDataSource() { + scoped_ptr<content::WebUIDataSource> source( + content::WebUIDataSource::Create(chrome::kChromeUIAppListStartPageHost)); + + source->SetUseJsonJSFormatV2(); + source->SetJsonPath("strings.js"); + + source->AddResourcePath("start_page.css", IDR_APP_LIST_START_PAGE_CSS); + source->AddResourcePath("start_page.js", IDR_APP_LIST_START_PAGE_JS); + source->SetDefaultResource(IDR_APP_LIST_START_PAGE_HTML); + + content::WebUIDataSource::Add(Profile::FromWebUI(web_ui()), source.release()); +} + +} // namespace app_list diff --git a/chrome/browser/ui/webui/app_list/start_page_ui.h b/chrome/browser/ui/webui/app_list/start_page_ui.h new file mode 100644 index 0000000..63a5252 --- /dev/null +++ b/chrome/browser/ui/webui/app_list/start_page_ui.h @@ -0,0 +1,28 @@ +// Copyright 2013 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_WEBUI_APP_LIST_START_PAGE_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_APP_LIST_START_PAGE_UI_H_ + +#include "base/basictypes.h" +#include "content/public/browser/web_ui_controller.h" + +namespace app_list { + +// StartPageUI for the app launcher start page. +class StartPageUI : public content::WebUIController { + public: + explicit StartPageUI(content::WebUI* web_ui); + virtual ~StartPageUI(); + + private: + // Initializes the data source used for this webui. + void InitDataSource(); + + DISALLOW_COPY_AND_ASSIGN(StartPageUI); +}; + +} // namespace app_list + +#endif // CHROME_BROWSER_UI_WEBUI_APP_LIST_START_PAGE_UI_H_ diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc index dbaf799..bcd3c9d 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc @@ -136,6 +136,10 @@ #include "chrome/browser/ui/webui/local_discovery/local_discovery_ui.h" #endif +#if defined(ENABLE_APP_LIST) +#include "chrome/browser/ui/webui/app_list/start_page_ui.h" +#endif + using content::WebUI; using content::WebUIController; using ui::ExternalWebDialogUI; @@ -290,6 +294,10 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui, if (url.host() == chrome::kChromeUIWebRtcLogsHost) return &NewWebUI<WebRtcLogsUI>; #endif +#if defined(ENABLE_APP_LIST) + if (url.host() == chrome::kChromeUIAppListStartPageHost) + return &NewWebUI<app_list::StartPageUI>; +#endif /**************************************************************************** * OS Specific #defines |