| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/ui/ash/app_list/apps_model_builder.h" | |
| 6 | |
| 7 #include "base/i18n/case_conversion.h" | |
| 8 #include "base/utf_string_conversions.h" | |
| 9 #include "chrome/browser/browser_process.h" | |
| 10 #include "chrome/browser/extensions/extension_service.h" | |
| 11 #include "chrome/browser/profiles/profile.h" | |
| 12 #include "chrome/browser/ui/ash/app_list/extension_app_item.h" | |
| 13 #include "chrome/common/chrome_notification_types.h" | |
| 14 #include "chrome/common/extensions/extension.h" | |
| 15 #include "content/public/browser/notification_service.h" | |
| 16 #include "ui/base/l10n/l10n_util_collator.h" | |
| 17 | |
| 18 using extensions::Extension; | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 const char* kSpecialApps[] = { | |
| 23 extension_misc::kChromeAppId, | |
| 24 extension_misc::kWebStoreAppId, | |
| 25 }; | |
| 26 | |
| 27 // ModelItemSortData provides a string key to sort with | |
| 28 // l10n_util::StringComparator. | |
| 29 struct ModelItemSortData { | |
| 30 explicit ModelItemSortData(app_list::AppListItemModel* app) | |
| 31 : app(app), | |
| 32 key(base::i18n::ToLower(UTF8ToUTF16(app->title()))) { | |
| 33 } | |
| 34 | |
| 35 // Needed by StringComparator<Element> in SortVectorWithStringKey, which uses | |
| 36 // this method to get a key to sort. | |
| 37 const string16& GetStringKey() const { | |
| 38 return key; | |
| 39 } | |
| 40 | |
| 41 app_list::AppListItemModel* app; | |
| 42 string16 key; | |
| 43 }; | |
| 44 | |
| 45 // Returns true if given |extension_id| is listed in kSpecialApps. | |
| 46 bool IsSpecialApp(const std::string& extension_id) { | |
| 47 for (size_t i = 0; i < arraysize(kSpecialApps); ++i) { | |
| 48 if (extension_id == kSpecialApps[i]) | |
| 49 return true; | |
| 50 } | |
| 51 | |
| 52 return false; | |
| 53 } | |
| 54 | |
| 55 } // namespace | |
| 56 | |
| 57 AppsModelBuilder::AppsModelBuilder(Profile* profile, | |
| 58 app_list::AppListModel::Apps* model, | |
| 59 AppListController* controller) | |
| 60 : profile_(profile), | |
| 61 controller_(controller), | |
| 62 model_(model), | |
| 63 special_apps_count_(0) { | |
| 64 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, | |
| 65 content::Source<Profile>(profile_)); | |
| 66 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, | |
| 67 content::Source<Profile>(profile_)); | |
| 68 registrar_.Add(this, chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST, | |
| 69 content::Source<Profile>(profile_)); | |
| 70 } | |
| 71 | |
| 72 AppsModelBuilder::~AppsModelBuilder() { | |
| 73 } | |
| 74 | |
| 75 void AppsModelBuilder::Build() { | |
| 76 DCHECK(model_ && model_->item_count() == 0); | |
| 77 CreateSpecialApps(); | |
| 78 | |
| 79 Apps apps; | |
| 80 GetExtensionApps(&apps); | |
| 81 | |
| 82 SortAndPopulateModel(apps); | |
| 83 HighlightApp(); | |
| 84 } | |
| 85 | |
| 86 void AppsModelBuilder::SortAndPopulateModel(const Apps& apps) { | |
| 87 // Just return if there is nothing to populate. | |
| 88 if (apps.empty()) | |
| 89 return; | |
| 90 | |
| 91 // Sort apps case insensitive alphabetically. | |
| 92 std::vector<ModelItemSortData> sorted; | |
| 93 for (Apps::const_iterator it = apps.begin(); it != apps.end(); ++it) | |
| 94 sorted.push_back(ModelItemSortData(*it)); | |
| 95 | |
| 96 l10n_util::SortVectorWithStringKey(g_browser_process->GetApplicationLocale(), | |
| 97 &sorted, | |
| 98 false /* needs_stable_sort */); | |
| 99 for (std::vector<ModelItemSortData>::const_iterator it = sorted.begin(); | |
| 100 it != sorted.end(); | |
| 101 ++it) { | |
| 102 model_->Add(it->app); | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 void AppsModelBuilder::InsertItemByTitle(app_list::AppListItemModel* app) { | |
| 107 DCHECK(model_); | |
| 108 | |
| 109 icu::Locale locale(g_browser_process->GetApplicationLocale().c_str()); | |
| 110 UErrorCode error = U_ZERO_ERROR; | |
| 111 scoped_ptr<icu::Collator> collator( | |
| 112 icu::Collator::createInstance(locale, error)); | |
| 113 if (U_FAILURE(error)) | |
| 114 collator.reset(); | |
| 115 | |
| 116 l10n_util::StringComparator<string16> c(collator.get()); | |
| 117 ModelItemSortData data(app); | |
| 118 for (size_t i = special_apps_count_; i < model_->item_count(); ++i) { | |
| 119 ModelItemSortData current(model_->GetItemAt(i)); | |
| 120 if (!c(current.key, data.key)) { | |
| 121 model_->AddAt(i, app); | |
| 122 return; | |
| 123 } | |
| 124 } | |
| 125 model_->Add(app); | |
| 126 } | |
| 127 | |
| 128 void AppsModelBuilder::GetExtensionApps(Apps* apps) { | |
| 129 DCHECK(profile_); | |
| 130 ExtensionService* service = profile_->GetExtensionService(); | |
| 131 if (!service) | |
| 132 return; | |
| 133 | |
| 134 // Get extension apps. | |
| 135 const ExtensionSet* extensions = service->extensions(); | |
| 136 for (ExtensionSet::const_iterator app = extensions->begin(); | |
| 137 app != extensions->end(); ++app) { | |
| 138 if ((*app)->ShouldDisplayInLauncher() && | |
| 139 !IsSpecialApp((*app)->id())) { | |
| 140 apps->push_back(new ExtensionAppItem(profile_, *app, controller_)); | |
| 141 } | |
| 142 } | |
| 143 } | |
| 144 | |
| 145 void AppsModelBuilder::CreateSpecialApps() { | |
| 146 DCHECK(model_ && model_->item_count() == 0); | |
| 147 | |
| 148 bool is_guest_session = Profile::IsGuestSession(); | |
| 149 ExtensionService* service = profile_->GetExtensionService(); | |
| 150 DCHECK(service); | |
| 151 for (size_t i = 0; i < arraysize(kSpecialApps); ++i) { | |
| 152 const std::string extension_id(kSpecialApps[i]); | |
| 153 if (is_guest_session && extension_id == extension_misc::kWebStoreAppId) | |
| 154 continue; | |
| 155 | |
| 156 const Extension* extension = service->GetInstalledExtension(extension_id); | |
| 157 DCHECK(extension); | |
| 158 | |
| 159 model_->Add(new ExtensionAppItem(profile_, extension, controller_)); | |
| 160 } | |
| 161 | |
| 162 special_apps_count_ = model_->item_count(); | |
| 163 } | |
| 164 | |
| 165 int AppsModelBuilder::FindApp(const std::string& app_id) { | |
| 166 DCHECK(model_); | |
| 167 for (size_t i = special_apps_count_; i < model_->item_count(); ++i) { | |
| 168 ChromeAppListItem* app = | |
| 169 static_cast<ChromeAppListItem*>(model_->GetItemAt(i)); | |
| 170 if (app->type() != ChromeAppListItem::TYPE_APP) | |
| 171 continue; | |
| 172 | |
| 173 ExtensionAppItem* extension_item = static_cast<ExtensionAppItem*>(app); | |
| 174 if (extension_item->extension_id() == app_id) | |
| 175 return i; | |
| 176 } | |
| 177 | |
| 178 return -1; | |
| 179 } | |
| 180 | |
| 181 void AppsModelBuilder::HighlightApp() { | |
| 182 DCHECK(model_); | |
| 183 if (highlight_app_id_.empty()) | |
| 184 return; | |
| 185 | |
| 186 int index = FindApp(highlight_app_id_); | |
| 187 if (index == -1) | |
| 188 return; | |
| 189 | |
| 190 model_->GetItemAt(index)->SetHighlighted(true); | |
| 191 highlight_app_id_.clear(); | |
| 192 } | |
| 193 | |
| 194 void AppsModelBuilder::Observe(int type, | |
| 195 const content::NotificationSource& source, | |
| 196 const content::NotificationDetails& details) { | |
| 197 switch (type) { | |
| 198 case chrome::NOTIFICATION_EXTENSION_LOADED: { | |
| 199 const Extension* extension = | |
| 200 content::Details<const Extension>(details).ptr(); | |
| 201 if (!extension->ShouldDisplayInLauncher()) | |
| 202 return; | |
| 203 | |
| 204 if (FindApp(extension->id()) != -1) | |
| 205 return; | |
| 206 | |
| 207 InsertItemByTitle(new ExtensionAppItem(profile_, extension, controller_)); | |
| 208 HighlightApp(); | |
| 209 break; | |
| 210 } | |
| 211 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { | |
| 212 const Extension* extension = | |
| 213 content::Details<extensions::UnloadedExtensionInfo>( | |
| 214 details)->extension; | |
| 215 int index = FindApp(extension->id()); | |
| 216 if (index >= 0) | |
| 217 model_->DeleteAt(index); | |
| 218 break; | |
| 219 } | |
| 220 case chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST: { | |
| 221 highlight_app_id_ = *content::Details<const std::string>(details).ptr(); | |
| 222 HighlightApp(); | |
| 223 break; | |
| 224 } | |
| 225 default: | |
| 226 NOTREACHED(); | |
| 227 } | |
| 228 } | |
| OLD | NEW |