Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(620)

Side by Side Diff: chrome/browser/ui/app_list/apps_model_builder.cc

Issue 10957048: app_list: Use NTP ordering in ExtensionSorting to sort apps. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/app_list/apps_model_builder.h" 5 #include "chrome/browser/ui/app_list/apps_model_builder.h"
6 6
7 #include "base/i18n/case_conversion.h" 7 #include <algorithm>
8 #include "base/utf_string_conversions.h" 8 #include <vector>
9 #include "chrome/browser/browser_process.h" 9
10 #include "chrome/browser/extensions/extension_prefs.h"
10 #include "chrome/browser/extensions/extension_service.h" 11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/prefs/pref_service.h"
11 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/ui/app_list/extension_app_item.h" 14 #include "chrome/browser/ui/app_list/extension_app_item.h"
13 #include "chrome/common/chrome_notification_types.h" 15 #include "chrome/common/chrome_notification_types.h"
14 #include "chrome/common/extensions/extension.h" 16 #include "chrome/common/extensions/extension.h"
15 #include "content/public/browser/notification_service.h" 17 #include "content/public/browser/notification_service.h"
16 #include "ui/base/l10n/l10n_util_collator.h"
17 18
18 using extensions::Extension; 19 using extensions::Extension;
19 20
20 namespace { 21 namespace {
21 22
22 // TODO(benwells): Get the list of special apps from the controller. 23 typedef std::vector<ExtensionAppItem*> Apps;
23 const char* kSpecialApps[] = {
24 extension_misc::kChromeAppId,
25 extension_misc::kWebStoreAppId,
26 };
27 24
28 // ModelItemSortData provides a string key to sort with 25 bool AppPrecedes(const ExtensionAppItem* app1, const ExtensionAppItem* app2) {
29 // l10n_util::StringComparator. 26 const syncer::StringOrdinal& page1 = app1->GetPageOrdinal();
30 struct ModelItemSortData { 27 const syncer::StringOrdinal& page2 = app2->GetPageOrdinal();
31 explicit ModelItemSortData(app_list::AppListItemModel* app) 28 if (page1.LessThan(page2))
32 : app(app), 29 return true;
33 key(base::i18n::ToLower(UTF8ToUTF16(app->title()))) {
34 }
35 30
36 // Needed by StringComparator<Element> in SortVectorWithStringKey, which uses 31 if (page1.Equals(page2))
37 // this method to get a key to sort. 32 return app1->GetAppLaunchOrdinal().LessThan(app2->GetAppLaunchOrdinal());
38 const string16& GetStringKey() const {
39 return key;
40 }
41
42 app_list::AppListItemModel* app;
43 string16 key;
44 };
45
46 // Returns true if given |extension_id| is listed in kSpecialApps.
47 bool IsSpecialApp(const std::string& extension_id) {
48 for (size_t i = 0; i < arraysize(kSpecialApps); ++i) {
49 if (extension_id == kSpecialApps[i])
50 return true;
51 }
52 33
53 return false; 34 return false;
54 } 35 }
55 36
56 } // namespace 37 } // namespace
57 38
58 AppsModelBuilder::AppsModelBuilder(Profile* profile, 39 AppsModelBuilder::AppsModelBuilder(Profile* profile,
59 app_list::AppListModel::Apps* model, 40 app_list::AppListModel::Apps* model,
60 AppListController* controller) 41 AppListController* controller)
61 : profile_(profile), 42 : profile_(profile),
62 controller_(controller), 43 controller_(controller),
63 model_(model), 44 model_(model) {
64 special_apps_count_(0) { 45 extensions::ExtensionPrefs* extension_prefs =
46 profile_->GetExtensionService()->extension_prefs();
47
65 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, 48 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
66 content::Source<Profile>(profile_)); 49 content::Source<Profile>(profile_));
67 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 50 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
68 content::Source<Profile>(profile_)); 51 content::Source<Profile>(profile_));
52 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED,
53 content::Source<ExtensionSorting>(extension_prefs->extension_sorting()));
69 registrar_.Add(this, chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST, 54 registrar_.Add(this, chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST,
70 content::Source<Profile>(profile_)); 55 content::Source<Profile>(profile_));
56
57 pref_change_registrar_.Init(extension_prefs->pref_service());
58 pref_change_registrar_.Add(extensions::ExtensionPrefs::kExtensionsPref, this);
71 } 59 }
72 60
73 AppsModelBuilder::~AppsModelBuilder() { 61 AppsModelBuilder::~AppsModelBuilder() {
74 } 62 }
75 63
76 void AppsModelBuilder::Build() { 64 void AppsModelBuilder::Build() {
77 DCHECK(model_ && model_->item_count() == 0); 65 DCHECK(model_ && model_->item_count() == 0);
78 CreateSpecialApps();
79 66
80 Apps apps; 67 PopulateApps();
81 GetExtensionApps(&apps);
82
83 SortAndPopulateModel(apps);
84 HighlightApp(); 68 HighlightApp();
85 } 69 }
86 70
87 void AppsModelBuilder::SortAndPopulateModel(const Apps& apps) { 71 void AppsModelBuilder::PopulateApps() {
88 // Just return if there is nothing to populate.
89 if (apps.empty())
90 return;
91
92 // Sort apps case insensitive alphabetically.
93 std::vector<ModelItemSortData> sorted;
94 for (Apps::const_iterator it = apps.begin(); it != apps.end(); ++it)
95 sorted.push_back(ModelItemSortData(*it));
96
97 l10n_util::SortVectorWithStringKey(g_browser_process->GetApplicationLocale(),
98 &sorted,
99 false /* needs_stable_sort */);
100 for (std::vector<ModelItemSortData>::const_iterator it = sorted.begin();
101 it != sorted.end();
102 ++it) {
103 model_->Add(it->app);
104 }
105 }
106
107 void AppsModelBuilder::InsertItemByTitle(app_list::AppListItemModel* app) {
108 DCHECK(model_);
109
110 icu::Locale locale(g_browser_process->GetApplicationLocale().c_str());
111 UErrorCode error = U_ZERO_ERROR;
112 scoped_ptr<icu::Collator> collator(
113 icu::Collator::createInstance(locale, error));
114 if (U_FAILURE(error))
115 collator.reset();
116
117 l10n_util::StringComparator<string16> c(collator.get());
118 ModelItemSortData data(app);
119 for (size_t i = special_apps_count_; i < model_->item_count(); ++i) {
120 ModelItemSortData current(model_->GetItemAt(i));
121 if (!c(current.key, data.key)) {
122 model_->AddAt(i, app);
123 return;
124 }
125 }
126 model_->Add(app);
127 }
128
129 void AppsModelBuilder::GetExtensionApps(Apps* apps) {
130 DCHECK(profile_);
131 ExtensionService* service = profile_->GetExtensionService(); 72 ExtensionService* service = profile_->GetExtensionService();
132 if (!service) 73 if (!service)
133 return; 74 return;
134 75
135 // Get extension apps. 76 Apps apps;
136 const ExtensionSet* extensions = service->extensions(); 77 const ExtensionSet* extensions = service->extensions();
137 for (ExtensionSet::const_iterator app = extensions->begin(); 78 for (ExtensionSet::const_iterator app = extensions->begin();
138 app != extensions->end(); ++app) { 79 app != extensions->end(); ++app) {
139 if ((*app)->ShouldDisplayInLauncher() && 80 if ((*app)->ShouldDisplayInLauncher())
140 !IsSpecialApp((*app)->id())) { 81 apps.push_back(new ExtensionAppItem(profile_, *app, controller_));
141 apps->push_back(new ExtensionAppItem(profile_, *app, controller_));
142 }
143 } 82 }
144 83
145 #if defined(OS_CHROMEOS) 84 #if defined(OS_CHROMEOS)
146 // Explicitly add Talk extension if it's installed and enabled. 85 // Explicitly add Talk extension if it's installed and enabled.
147 // Add it here instead of in CreateSpecialApps() so it sorts naturally. 86 // Add it here instead of in CreateSpecialApps() so it sorts naturally.
148 // Prefer debug > alpha > beta > production version. 87 // Prefer debug > alpha > beta > production version.
149 const char* kTalkIds[] = { 88 const char* kTalkIds[] = {
150 extension_misc::kTalkDebugExtensionId, 89 extension_misc::kTalkDebugExtensionId,
151 extension_misc::kTalkAlphaExtensionId, 90 extension_misc::kTalkAlphaExtensionId,
152 extension_misc::kTalkBetaExtensionId, 91 extension_misc::kTalkBetaExtensionId,
153 extension_misc::kTalkExtensionId, 92 extension_misc::kTalkExtensionId,
154 }; 93 };
155 for (size_t i = 0; i < arraysize(kTalkIds); ++i) { 94 for (size_t i = 0; i < arraysize(kTalkIds); ++i) {
156 const Extension* talk = service->GetExtensionById( 95 const Extension* talk = service->GetExtensionById(
157 kTalkIds[i], false /*include_disabled*/); 96 kTalkIds[i], false /*include_disabled*/);
158 if (talk) { 97 if (talk) {
159 apps->push_back(new ExtensionAppItem(profile_, talk, controller_)); 98 apps.push_back(new ExtensionAppItem(profile_, talk, controller_));
160 break; 99 break;
161 } 100 }
162 } 101 }
163 #endif // OS_CHROMEOS 102 #endif // OS_CHROMEOS
103
104 if (apps.empty())
105 return;
106
107 std::sort(apps.begin(), apps.end(), &AppPrecedes);
108
109 for (Apps::const_iterator it = apps.begin();
110 it != apps.end();
111 ++it) {
112 model_->Add(*it);
113 }
164 } 114 }
165 115
166 void AppsModelBuilder::CreateSpecialApps() { 116 void AppsModelBuilder::ResortApps() {
167 DCHECK(model_ && model_->item_count() == 0); 117 Apps apps;
118 for (size_t i = 0; i < model_->item_count(); ++i)
119 apps.push_back(GetAppAt(i));
168 120
169 bool is_guest_session = Profile::IsGuestSession(); 121 if (apps.empty())
170 ExtensionService* service = profile_->GetExtensionService(); 122 return;
171 DCHECK(service);
172 for (size_t i = 0; i < arraysize(kSpecialApps); ++i) {
173 const std::string extension_id(kSpecialApps[i]);
174 if (is_guest_session && extension_id == extension_misc::kWebStoreAppId)
175 continue;
176 123
177 const Extension* extension = service->GetInstalledExtension(extension_id); 124 std::sort(apps.begin(), apps.end(), &AppPrecedes);
178 if (!extension)
179 continue;
180 125
181 model_->Add(new ExtensionAppItem(profile_, extension, controller_)); 126 // Adjusts the order of apps as needed in |model_| based on |apps|.
127 for (size_t i = 0; i < apps.size(); ++i) {
128 ExtensionAppItem* app_item = apps[i];
129
130 const size_t insert_index = i;
131
132 bool found = false;
133 size_t index = 0;
134
135 // Finds |app_item| in remaining unsorted part in |model_|.
136 for (size_t j = insert_index; j < model_->item_count(); ++j) {
137 if (GetAppAt(j) == app_item) {
138 found = true;
139 index = j;
140 break;
141 }
142 }
143
144 if (found) {
145 if (index != insert_index) {
146 model_->RemoveAt(index);
147 model_->AddAt(insert_index, app_item);
148 }
149 } else {
150 NOTREACHED();
151 }
182 } 152 }
153 }
183 154
184 special_apps_count_ = model_->item_count(); 155 void AppsModelBuilder::InsertApp(ExtensionAppItem* app) {
156 DCHECK(model_);
157
158 size_t start = 0;
159 size_t end = model_->item_count();
160 while (start < end) {
161 size_t mid = (start + end) / 2;
162
163 if (AppPrecedes(GetAppAt(mid), app))
164 start = mid + 1;
165 else
166 end = mid;
167 }
168 model_->AddAt(start, app);
185 } 169 }
186 170
187 int AppsModelBuilder::FindApp(const std::string& app_id) { 171 int AppsModelBuilder::FindApp(const std::string& app_id) {
188 DCHECK(model_); 172 DCHECK(model_);
189 for (size_t i = special_apps_count_; i < model_->item_count(); ++i) {
190 ChromeAppListItem* app =
191 static_cast<ChromeAppListItem*>(model_->GetItemAt(i));
192 if (app->type() != ChromeAppListItem::TYPE_APP)
193 continue;
194 173
195 ExtensionAppItem* extension_item = static_cast<ExtensionAppItem*>(app); 174 for (size_t i = 0; i < model_->item_count(); ++i) {
196 if (extension_item->extension_id() == app_id) 175 if (GetAppAt(i)->extension_id() == app_id)
197 return i; 176 return i;
198 } 177 }
199 178
200 return -1; 179 return -1;
201 } 180 }
202 181
203 void AppsModelBuilder::HighlightApp() { 182 void AppsModelBuilder::HighlightApp() {
204 DCHECK(model_); 183 DCHECK(model_);
205 if (highlight_app_id_.empty()) 184 if (highlight_app_id_.empty())
206 return; 185 return;
207 186
208 int index = FindApp(highlight_app_id_); 187 int index = FindApp(highlight_app_id_);
209 if (index == -1) 188 if (index == -1)
210 return; 189 return;
211 190
212 model_->GetItemAt(index)->SetHighlighted(true); 191 model_->GetItemAt(index)->SetHighlighted(true);
213 highlight_app_id_.clear(); 192 highlight_app_id_.clear();
214 } 193 }
215 194
195 ExtensionAppItem* AppsModelBuilder::GetAppAt(size_t index) {
196 DCHECK_LT(index, model_->item_count());
197 ChromeAppListItem* item =
198 static_cast<ChromeAppListItem*>(model_->GetItemAt(index));
199 DCHECK_EQ(item->type(), ChromeAppListItem::TYPE_APP);
200
201 return static_cast<ExtensionAppItem*>(item);
202 }
203
216 void AppsModelBuilder::Observe(int type, 204 void AppsModelBuilder::Observe(int type,
217 const content::NotificationSource& source, 205 const content::NotificationSource& source,
218 const content::NotificationDetails& details) { 206 const content::NotificationDetails& details) {
219 switch (type) { 207 switch (type) {
220 case chrome::NOTIFICATION_EXTENSION_LOADED: { 208 case chrome::NOTIFICATION_EXTENSION_LOADED: {
221 const Extension* extension = 209 const Extension* extension =
222 content::Details<const Extension>(details).ptr(); 210 content::Details<const Extension>(details).ptr();
223 if (!extension->ShouldDisplayInLauncher()) 211 if (!extension->ShouldDisplayInLauncher())
224 return; 212 return;
225 213
226 if (FindApp(extension->id()) != -1) 214 if (FindApp(extension->id()) != -1)
227 return; 215 return;
228 216
229 InsertItemByTitle(new ExtensionAppItem(profile_, extension, controller_)); 217 InsertApp(new ExtensionAppItem(profile_, extension, controller_));
230 HighlightApp(); 218 HighlightApp();
231 break; 219 break;
232 } 220 }
233 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { 221 case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
234 const Extension* extension = 222 const Extension* extension =
235 content::Details<extensions::UnloadedExtensionInfo>( 223 content::Details<extensions::UnloadedExtensionInfo>(
236 details)->extension; 224 details)->extension;
237 int index = FindApp(extension->id()); 225 int index = FindApp(extension->id());
238 if (index >= 0) 226 if (index >= 0)
239 model_->DeleteAt(index); 227 model_->DeleteAt(index);
240 break; 228 break;
241 } 229 }
230 case chrome::NOTIFICATION_EXTENSION_LAUNCHER_REORDERED: {
231 ResortApps();
232 break;
233 }
242 case chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST: { 234 case chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST: {
243 highlight_app_id_ = *content::Details<const std::string>(details).ptr(); 235 highlight_app_id_ = *content::Details<const std::string>(details).ptr();
244 HighlightApp(); 236 HighlightApp();
245 break; 237 break;
246 } 238 }
239 case chrome::NOTIFICATION_PREF_CHANGED: {
240 ResortApps();
241 break;
242 }
247 default: 243 default:
248 NOTREACHED(); 244 NOTREACHED();
249 } 245 }
250 } 246 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/app_list/apps_model_builder.h ('k') | chrome/browser/ui/app_list/apps_model_builder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698