14e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
24e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
34e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)// found in the LICENSE file.
44e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
54e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/ui/app_list/recommended_apps.h"
64e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
74e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include <algorithm>
84e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include <vector>
94e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "base/bind.h"
11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/extensions/extension_ui_util.h"
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/browser/ui/app_list/recommended_apps_observer.h"
144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#include "chrome/common/pref_names.h"
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_prefs.h"
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "extensions/browser/extension_registry.h"
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/pref_names.h"
18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/common/extension_set.h"
204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace app_list {
224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)namespace {
244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)struct AppSortInfo {
264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  AppSortInfo() : app(NULL) {}
274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  AppSortInfo(const extensions::Extension* app,
284e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)              const base::Time& last_launch_time)
294e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      : app(app), last_launch_time(last_launch_time) {}
304e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const extensions::Extension* app;
324e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  base::Time last_launch_time;
334e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)};
344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
354e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)bool AppLaunchedMoreRecent(const AppSortInfo& app1, const AppSortInfo& app2) {
364e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  return app1.last_launch_time > app2.last_launch_time;
374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
384e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
394e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}  // namespace
404e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)RecommendedApps::RecommendedApps(Profile* profile)
4246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    : profile_(profile), extension_registry_observer_(this) {
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_);
444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  pref_change_registrar_.Init(prefs->pref_service());
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  pref_change_registrar_.Add(extensions::pref_names::kExtensions,
464e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                             base::Bind(&RecommendedApps::Update,
474e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                                        base::Unretained(this)));
4846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(profile));
494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  Update();
504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
514e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)RecommendedApps::~RecommendedApps() {
534e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
554e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void RecommendedApps::AddObserver(RecommendedAppsObserver* observer) {
564e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  observers_.AddObserver(observer);
574e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
584e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
594e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void RecommendedApps::RemoveObserver(RecommendedAppsObserver* observer) {
604e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  observers_.RemoveObserver(observer);
614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void RecommendedApps::Update() {
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_);
654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::vector<AppSortInfo> sorted_apps;
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  const extensions::ExtensionSet& extensions =
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      extensions::ExtensionRegistry::Get(profile_)->enabled_extensions();
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  for (extensions::ExtensionSet::const_iterator app = extensions.begin();
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)       app != extensions.end();
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)       ++app) {
721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!extensions::ui_util::ShouldDisplayInAppLauncher(app->get(), profile_))
734e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      continue;
744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    sorted_apps.push_back(
764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        AppSortInfo(app->get(), prefs->GetLastLaunchTime((*app)->id())));
774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  std::sort(sorted_apps.begin(), sorted_apps.end(), &AppLaunchedMoreRecent);
804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const size_t kMaxRecommendedApps = 4;
824e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  sorted_apps.resize(std::min(kMaxRecommendedApps, sorted_apps.size()));
834e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
844e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  Apps new_recommends;
854e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  for (size_t i = 0; i < sorted_apps.size(); ++i)
864e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    new_recommends.push_back(sorted_apps[i].app);
874e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
884e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  const bool changed = apps_.size() != new_recommends.size() ||
894e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      !std::equal(apps_.begin(), apps_.end(), new_recommends.begin());
904e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (changed) {
914e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    apps_.swap(new_recommends);
924e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    FOR_EACH_OBSERVER(
934e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)        RecommendedAppsObserver, observers_, OnRecommendedAppsChanged());
944e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
964e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
9746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void RecommendedApps::OnExtensionWillBeInstalled(
9846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    content::BrowserContext* browser_context,
9946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    const extensions::Extension* extension,
10046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    bool is_update,
10146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    bool from_ephemeral,
10246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    const std::string& old_name) {
1034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  Update();
1044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void RecommendedApps::OnExtensionLoaded(
10746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    content::BrowserContext* browser_context,
1084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const extensions::Extension* extension) {
1094e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  Update();
1104e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1114e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void RecommendedApps::OnExtensionUnloaded(
11346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    content::BrowserContext* browser_context,
11446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    const extensions::Extension* extension,
11546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    extensions::UnloadedExtensionInfo::Reason reason) {
1164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  Update();
1174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void RecommendedApps::OnExtensionUninstalled(
12046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    content::BrowserContext* browser_context,
1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const extensions::Extension* extension,
1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    extensions::UninstallReason reason) {
1234e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  Update();
1244e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
1254e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
1264e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}  // namespace app_list
127