diff options
author | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-18 02:22:15 +0000 |
---|---|---|
committer | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-18 02:22:15 +0000 |
commit | d42ef8acdf908a348d0aec5a4814b6257f42276c (patch) | |
tree | b7ec497d6ffe57611f9f07626060845edbf5f8be | |
parent | c8d6b485995362620025e3b7ad74772beac803d7 (diff) | |
download | chromium_src-d42ef8acdf908a348d0aec5a4814b6257f42276c.zip chromium_src-d42ef8acdf908a348d0aec5a4814b6257f42276c.tar.gz chromium_src-d42ef8acdf908a348d0aec5a4814b6257f42276c.tar.bz2 |
chromeos: Sync animation.
- Pulse app list button while LauncherModel has STATUS_LOADING;
- Remove LauncherItemStatus::STATUS_IS_PENDING etc since it is no longer needed;
- ChromeLauncherController sets LauncherModel::STATUS_LOADING on starting and
watch for sync finish and pending extension install. It sets
LauncherModel::STATUS_NORMAL when sync is finished and there is no pending
extension install from sync, or when a maximum 60 seconds timeout since
turning on loading status;
BUG=129236
TEST=Verify sync animation after OOBE.
Review URL: https://chromiumcodereview.appspot.com/10829268
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@152221 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ash/ash_strings.grd | 3 | ||||
-rw-r--r-- | ash/launcher/app_list_button.cc | 98 | ||||
-rw-r--r-- | ash/launcher/app_list_button.h | 5 | ||||
-rw-r--r-- | ash/launcher/launcher_button.cc | 107 | ||||
-rw-r--r-- | ash/launcher/launcher_button.h | 12 | ||||
-rw-r--r-- | ash/launcher/launcher_model.cc | 12 | ||||
-rw-r--r-- | ash/launcher/launcher_model.h | 10 | ||||
-rw-r--r-- | ash/launcher/launcher_model_observer.h | 5 | ||||
-rw-r--r-- | ash/launcher/launcher_model_unittest.cc | 5 | ||||
-rw-r--r-- | ash/launcher/launcher_types.h | 5 | ||||
-rw-r--r-- | ash/launcher/launcher_view.cc | 53 | ||||
-rw-r--r-- | ash/launcher/launcher_view.h | 1 | ||||
-rw-r--r-- | ash/launcher/launcher_view_unittest.cc | 6 | ||||
-rw-r--r-- | chrome/browser/extensions/pending_extension_manager.cc | 16 | ||||
-rw-r--r-- | chrome/browser/extensions/pending_extension_manager.h | 3 | ||||
-rw-r--r-- | chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc | 129 | ||||
-rw-r--r-- | chrome/browser/ui/ash/launcher/chrome_launcher_controller.h | 30 | ||||
-rw-r--r-- | chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc | 22 | ||||
-rw-r--r-- | chrome/browser/ui/ash/launcher/launcher_context_menu.cc | 33 |
19 files changed, 279 insertions, 276 deletions
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index e48d9d7..3364014 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd @@ -167,6 +167,9 @@ This file contains the strings for ash. <message name="IDS_AURA_APP_LIST_TITLE" desc="The title used for the Aura app list in the launcher"> Apps </message> + <message name="IDS_AURA_APP_LIST_SYNCING_TITLE" desc="The title used for the Aura app list in the launcher to indicate loading/syncing apps."> + Syncing apps... + </message> <message name="IDS_AURA_NEW_TAB" desc="The text label of the New Tab menu item"> New tab </message> diff --git a/ash/launcher/app_list_button.cc b/ash/launcher/app_list_button.cc index 14e80e5..d38fe22 100644 --- a/ash/launcher/app_list_button.cc +++ b/ash/launcher/app_list_button.cc @@ -4,35 +4,113 @@ #include "ash/launcher/app_list_button.h" +#include <vector> + #include "ash/launcher/launcher_button_host.h" +#include "ash/launcher/launcher_types.h" +#include "grit/ash_strings.h" +#include "grit/ui_resources.h" #include "ui/base/accessibility/accessible_view_state.h" -#include "ui/base/animation/animation_delegate.h" -#include "ui/base/animation/throb_animation.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/compositor/layer.h" #include "ui/compositor/layer_animation_element.h" -#include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer_animation_sequence.h" #include "ui/compositor/scoped_layer_animation_settings.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/image/image.h" -#include "ui/gfx/shadow_value.h" -#include "ui/gfx/skbitmap_operations.h" #include "ui/gfx/transform_util.h" -#include "ui/views/controls/image_view.h" namespace ash { - namespace internal { +namespace { + +const int kAnimationDurationInMs = 600; +const float kAnimationOpacity[] = { 0.4f, 0.8f, 0.4f }; +const float kAnimationScale[] = { 0.8f, 1.0f, 0.8f }; + +} // namespace + AppListButton::AppListButton(views::ButtonListener* listener, LauncherButtonHost* host) : views::ImageButton(listener), - host_(host) {} + host_(host) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + SetImage( + views::CustomButton::BS_NORMAL, + rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST).ToImageSkia()); + SetImage( + views::CustomButton::BS_HOT, + rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_HOT). + ToImageSkia()); + SetImage( + views::CustomButton::BS_PUSHED, + rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_PUSHED). + ToImageSkia()); + SetAccessibleName(l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE)); + SetSize(gfx::Size(kLauncherPreferredSize, kLauncherPreferredSize)); +} AppListButton::~AppListButton() { } +void AppListButton::StartLoadingAnimation() { + // The two animation set should have the same size. + DCHECK_EQ(arraysize(kAnimationOpacity), arraysize(kAnimationScale)); + + layer()->GetAnimator()->StopAnimating(); + + scoped_ptr<ui::LayerAnimationSequence> opacity_sequence( + new ui::LayerAnimationSequence()); + scoped_ptr<ui::LayerAnimationSequence> transform_sequence( + new ui::LayerAnimationSequence()); + + // The animations loop infinitely. + opacity_sequence->set_is_cyclic(true); + transform_sequence->set_is_cyclic(true); + + for (size_t i = 0; i < arraysize(kAnimationOpacity); ++i) { + opacity_sequence->AddElement( + ui::LayerAnimationElement::CreateOpacityElement( + kAnimationOpacity[i], + base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); + transform_sequence->AddElement( + ui::LayerAnimationElement::CreateTransformElement( + ui::GetScaleTransform(GetLocalBounds().CenterPoint(), + kAnimationScale[i]), + base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); + } + + ui::LayerAnimationElement::AnimatableProperties opacity_properties; + opacity_properties.insert(ui::LayerAnimationElement::OPACITY); + opacity_sequence->AddElement( + ui::LayerAnimationElement::CreatePauseElement( + opacity_properties, + base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); + + ui::LayerAnimationElement::AnimatableProperties transform_properties; + transform_properties.insert(ui::LayerAnimationElement::TRANSFORM); + transform_sequence->AddElement( + ui::LayerAnimationElement::CreatePauseElement( + transform_properties, + base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); + + std::vector<ui::LayerAnimationSequence*> animations; + // LayerAnimator::ScheduleTogether takes ownership of the sequences. + animations.push_back(opacity_sequence.release()); + animations.push_back(transform_sequence.release()); + layer()->GetAnimator()->ScheduleTogether(animations); +} + +void AppListButton::StopLoadingAnimation() { + layer()->GetAnimator()->StopAnimating(); + + ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator()); + settings.SetTransitionDuration( + base::TimeDelta::FromMilliseconds(kAnimationDurationInMs)); + layer()->SetOpacity(1.0f); + layer()->SetTransform(ui::Transform()); +} + bool AppListButton::OnMousePressed(const ui::MouseEvent& event) { ImageButton::OnMousePressed(event); host_->PointerPressedOnButton(this, LauncherButtonHost::MOUSE, event); diff --git a/ash/launcher/app_list_button.h b/ash/launcher/app_list_button.h index ee18557..4d25254 100644 --- a/ash/launcher/app_list_button.h +++ b/ash/launcher/app_list_button.h @@ -19,6 +19,9 @@ class AppListButton : public views::ImageButton { LauncherButtonHost* host); virtual ~AppListButton(); + void StartLoadingAnimation(); + void StopLoadingAnimation(); + protected: // View overrides: virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE; @@ -39,4 +42,4 @@ class AppListButton : public views::ImageButton { } // namespace internal } // namespace ash -#endif // ASH_LAUNCHER_LAUNCHER_BUTTON_H_ +#endif // ASH_LAUNCHER_APP_LIST_BUTTON_H_ diff --git a/ash/launcher/launcher_button.cc b/ash/launcher/launcher_button.cc index b550d56d..c0d0685 100644 --- a/ash/launcher/launcher_button.cc +++ b/ash/launcher/launcher_button.cc @@ -5,7 +5,6 @@ #include "ash/launcher/launcher_button.h" #include <algorithm> -#include <vector> #include "ash/launcher/launcher_button_host.h" #include "grit/ui_resources.h" @@ -16,14 +15,10 @@ #include "ui/base/events.h" #include "ui/base/resource/resource_bundle.h" #include "ui/compositor/layer.h" -#include "ui/compositor/layer_animation_element.h" -#include "ui/compositor/layer_animation_observer.h" -#include "ui/compositor/layer_animation_sequence.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_skia_operations.h" -#include "ui/gfx/transform_util.h" #include "ui/views/controls/image_view.h" namespace { @@ -100,102 +95,6 @@ class LauncherButton::BarView : public views::ImageView, }; //////////////////////////////////////////////////////////////////////////////// -// LauncherButton::IconPulseAnimation - -// IconPulseAnimation plays a pulse animation in a loop for given |icon_view|. -// It iterates through all animations, wait for one duration then starts again. -class LauncherButton::IconPulseAnimation { - public: - explicit IconPulseAnimation(IconView* icon_view) - : icon_view_(icon_view) { - SchedulePulseAnimations(); - } - virtual ~IconPulseAnimation() { - // Restore icon_view_ on destruction. - ScheduleRestoreAnimation(); - } - - private: - // Animation duration in millisecond. - static const int kAnimationDurationInMs; - - // Number of animations to run and animation parameters. - static const float kAnimationOpacity[]; - static const float kAnimationScale[]; - - // Schedules pulse animations. - void SchedulePulseAnimations(); - - // Schedule an animation to restore the view to normal state. - void ScheduleRestoreAnimation(); - - IconView* icon_view_; // Owned by views hierarchy of LauncherButton. - - DISALLOW_COPY_AND_ASSIGN(IconPulseAnimation); -}; - -// static -const int LauncherButton::IconPulseAnimation::kAnimationDurationInMs = 600; -const float LauncherButton::IconPulseAnimation::kAnimationOpacity[] = - { 0.4f, 0.8f }; -const float LauncherButton::IconPulseAnimation::kAnimationScale[] = - { 0.8f, 1.0f }; - -void LauncherButton::IconPulseAnimation::SchedulePulseAnimations() { - // The two animation set should have the same size. - DCHECK(arraysize(kAnimationOpacity) == arraysize(kAnimationScale)); - - scoped_ptr<ui::LayerAnimationSequence> opacity_sequence( - new ui::LayerAnimationSequence()); - scoped_ptr<ui::LayerAnimationSequence> transform_sequence( - new ui::LayerAnimationSequence()); - - // The animations loop infinitely. - opacity_sequence->set_is_cyclic(true); - transform_sequence->set_is_cyclic(true); - - for (size_t i = 0; i < arraysize(kAnimationOpacity); ++i) { - opacity_sequence->AddElement( - ui::LayerAnimationElement::CreateOpacityElement( - kAnimationOpacity[i], - base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); - transform_sequence->AddElement( - ui::LayerAnimationElement::CreateTransformElement( - ui::GetScaleTransform(icon_view_->GetLocalBounds().CenterPoint(), - kAnimationScale[i]), - base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); - } - - ui::LayerAnimationElement::AnimatableProperties opacity_properties; - opacity_properties.insert(ui::LayerAnimationElement::OPACITY); - opacity_sequence->AddElement( - ui::LayerAnimationElement::CreatePauseElement( - opacity_properties, - base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); - - ui::LayerAnimationElement::AnimatableProperties transform_properties; - transform_properties.insert(ui::LayerAnimationElement::TRANSFORM); - transform_sequence->AddElement( - ui::LayerAnimationElement::CreatePauseElement( - transform_properties, - base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); - - std::vector<ui::LayerAnimationSequence*> animations; - // LayerAnimator::ScheduleTogether takes ownership of the sequences. - animations.push_back(opacity_sequence.release()); - animations.push_back(transform_sequence.release()); - icon_view_->layer()->GetAnimator()->ScheduleTogether(animations); -} - -// Schedule an animation to restore the view to normal state. -void LauncherButton::IconPulseAnimation::ScheduleRestoreAnimation() { - ui::Layer* layer = icon_view_->layer(); - ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); - layer->SetOpacity(1.0f); - layer->SetTransform(ui::Transform()); -} - -//////////////////////////////////////////////////////////////////////////////// // LauncherButton::IconView LauncherButton::IconView::IconView() : icon_size_(kIconSize) { @@ -293,8 +192,6 @@ void LauncherButton::AddState(State state) { } if (state & STATE_ATTENTION) bar_->ShowAttention(true); - if (state & STATE_PENDING) - icon_pulse_animation_.reset(new IconPulseAnimation(icon_view_)); } } @@ -314,8 +211,6 @@ void LauncherButton::ClearState(State state) { } if (state & STATE_ATTENTION) bar_->ShowAttention(false); - if (state & STATE_PENDING) - icon_pulse_animation_.reset(); } } @@ -461,7 +356,7 @@ bool LauncherButton::IsShelfHorizontal() const { } void LauncherButton::UpdateState() { - if (state_ == STATE_NORMAL || state_ & STATE_PENDING) { + if (state_ == STATE_NORMAL) { bar_->SetVisible(false); } else { int bar_id; diff --git a/ash/launcher/launcher_button.h b/ash/launcher/launcher_button.h index 161f13e..eb4a735 100644 --- a/ash/launcher/launcher_button.h +++ b/ash/launcher/launcher_button.h @@ -5,7 +5,6 @@ #ifndef ASH_LAUNCHER_LAUNCHER_BUTTON_H_ #define ASH_LAUNCHER_LAUNCHER_BUTTON_H_ -#include "base/memory/scoped_ptr.h" #include "ui/gfx/shadow_value.h" #include "ui/views/controls/button/custom_button.h" #include "ui/views/controls/image_view.h" @@ -32,11 +31,7 @@ class LauncherButton : public views::CustomButton { STATE_ACTIVE = 1 << 2, // Underlying LauncherItem needs user's attention. STATE_ATTENTION = 1 << 3, - // Underlying LauncherItem has pending operations. - // e.g. A TYPE_APP_SHORTCUT item whose corresponding app is being - // installed. - STATE_PENDING = 1 << 4, - STATE_FOCUSED = 1 << 5, + STATE_FOCUSED = 1 << 4, }; virtual ~LauncherButton(); @@ -107,7 +102,6 @@ class LauncherButton : public views::CustomButton { private: class BarView; - class IconPulseAnimation; // Returns true if the shelf is horizontal. If this returns false the shelf is // vertical. @@ -125,10 +119,6 @@ class LauncherButton : public views::CustomButton { // together. int state_; - // Runs a pulse animation for |icon_view_|. It is created when button state - // has a STATE_PENDING bit and destroyed when that bit is clear. - scoped_ptr<IconPulseAnimation> icon_pulse_animation_; - gfx::ShadowValues icon_shadows_; DISALLOW_COPY_AND_ASSIGN(LauncherButton); diff --git a/ash/launcher/launcher_model.cc b/ash/launcher/launcher_model.cc index d9aa8ad..2945a5d 100644 --- a/ash/launcher/launcher_model.cc +++ b/ash/launcher/launcher_model.cc @@ -7,7 +7,6 @@ #include <algorithm> #include "ash/launcher/launcher_model_observer.h" -#include "ui/aura/window.h" namespace ash { @@ -37,7 +36,7 @@ bool CompareByWeight(const LauncherItem& a, const LauncherItem& b) { } // namespace -LauncherModel::LauncherModel() : next_id_(1) { +LauncherModel::LauncherModel() : next_id_(1), status_(STATUS_NORMAL) { LauncherItem app_list; app_list.type = TYPE_APP_LIST; app_list.is_incognito = false; @@ -118,6 +117,15 @@ LauncherItems::const_iterator LauncherModel::ItemByID(int id) const { return items_.end(); } +void LauncherModel::SetStatus(Status status) { + if (status_ == status) + return; + + status_ = status; + FOR_EACH_OBSERVER(LauncherModelObserver, observers_, + LauncherStatusChanged()); +} + void LauncherModel::AddObserver(LauncherModelObserver* observer) { observers_.AddObserver(observer); } diff --git a/ash/launcher/launcher_model.h b/ash/launcher/launcher_model.h index d4cd05e..a733a1b 100644 --- a/ash/launcher/launcher_model.h +++ b/ash/launcher/launcher_model.h @@ -22,6 +22,12 @@ class LauncherModelObserver; // Model used by LauncherView. class ASH_EXPORT LauncherModel { public: + enum Status { + STATUS_NORMAL, + // A status that indicates apps are syncing/loading. + STATUS_LOADING, + }; + LauncherModel(); ~LauncherModel(); @@ -56,6 +62,9 @@ class ASH_EXPORT LauncherModel { const LauncherItems& items() const { return items_; } int item_count() const { return static_cast<int>(items_.size()); } + void SetStatus(Status status); + Status status() const { return status_; } + void AddObserver(LauncherModelObserver* observer); void RemoveObserver(LauncherModelObserver* observer); @@ -68,6 +77,7 @@ class ASH_EXPORT LauncherModel { // ID assigned to the next item. LauncherID next_id_; LauncherItems items_; + Status status_; ObserverList<LauncherModelObserver> observers_; DISALLOW_COPY_AND_ASSIGN(LauncherModel); diff --git a/ash/launcher/launcher_model_observer.h b/ash/launcher/launcher_model_observer.h index 5d73d1c..16067c0 100644 --- a/ash/launcher/launcher_model_observer.h +++ b/ash/launcher/launcher_model_observer.h @@ -25,10 +25,13 @@ class ASH_EXPORT LauncherModelObserver { // of the arguments. virtual void LauncherItemMoved(int start_index, int target_index) = 0; - // Invoked when the the state of an item changes. |old_item| is the item + // Invoked when the state of an item changes. |old_item| is the item // before the change. virtual void LauncherItemChanged(int index, const LauncherItem& old_item) = 0; + // Invoked when launcher status is changed. + virtual void LauncherStatusChanged() = 0; + protected: virtual ~LauncherModelObserver() {} }; diff --git a/ash/launcher/launcher_model_unittest.cc b/ash/launcher/launcher_model_unittest.cc index cc449eba..9a4fbcd 100644 --- a/ash/launcher/launcher_model_unittest.cc +++ b/ash/launcher/launcher_model_unittest.cc @@ -4,6 +4,9 @@ #include "ash/launcher/launcher_model.h" +#include <set> +#include <string> + #include "ash/launcher/launcher_model_observer.h" #include "base/stringprintf.h" #include "testing/gtest/include/gtest/gtest.h" @@ -48,6 +51,8 @@ class TestLauncherModelObserver : public LauncherModelObserver { virtual void LauncherItemMoved(int start_index, int target_index) OVERRIDE { moved_count_++; } + virtual void LauncherStatusChanged() OVERRIDE { + } private: void AddToResult(const std::string& format, int count, std::string* result) { diff --git a/ash/launcher/launcher_types.h b/ash/launcher/launcher_types.h index a6a6dc0..24110f4 100644 --- a/ash/launcher/launcher_types.h +++ b/ash/launcher/launcher_types.h @@ -56,11 +56,6 @@ enum LauncherItemStatus { STATUS_ACTIVE, // A LauncherItem that needs user's attention. STATUS_ATTENTION, - // A LauncherItem that has pending operations. - // e.g. A TYEE_APP_SHORTCUT item whose application is - // being installed/upgraded. - // Note STATUS_PENDING is a macro in WinNT.h on Windows. - STATUS_IS_PENDING, }; struct ASH_EXPORT LauncherItem { diff --git a/ash/launcher/launcher_view.cc b/ash/launcher/launcher_view.cc index 20fa176..9a72042 100644 --- a/ash/launcher/launcher_view.cc +++ b/ash/launcher/launcher_view.cc @@ -18,27 +18,20 @@ #include "ash/shell.h" #include "ash/shell_delegate.h" #include "base/auto_reset.h" -#include "base/utf_string_conversions.h" +#include "base/memory/scoped_ptr.h" #include "grit/ash_strings.h" #include "grit/ui_resources.h" -#include "ui/aura/window.h" -#include "ui/base/animation/animation.h" -#include "ui/base/animation/throb_animation.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/simple_menu_model.h" #include "ui/base/resource/resource_bundle.h" #include "ui/compositor/layer.h" -#include "ui/gfx/image/image.h" #include "ui/views/animation/bounds_animator.h" #include "ui/views/border.h" -#include "ui/views/controls/button/image_button.h" #include "ui/views/controls/menu/menu_model_adapter.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/focus/focus_search.h" #include "ui/views/view_model.h" #include "ui/views/view_model_utils.h" -#include "ui/views/widget/root_view.h" -#include "ui/views/widget/widget.h" using ui::Animation; using views::View; @@ -47,13 +40,13 @@ namespace ash { namespace internal { // Default amount content is inset on the left edge. -static const int kDefaultLeadingInset = 8; +const int kDefaultLeadingInset = 8; // Minimum distance before drag starts. -static const int kMinimumDragDistance = 8; +const int kMinimumDragDistance = 8; // Size between the buttons. -static const int kButtonSpacing = 4; +const int kButtonSpacing = 4; namespace { @@ -177,31 +170,21 @@ void ReflectItemStatus(const ash::LauncherItem& item, button->ClearState(LauncherButton::STATE_ACTIVE); button->ClearState(LauncherButton::STATE_RUNNING); button->ClearState(LauncherButton::STATE_ATTENTION); - button->ClearState(LauncherButton::STATE_PENDING); break; case STATUS_RUNNING: button->ClearState(LauncherButton::STATE_ACTIVE); button->AddState(LauncherButton::STATE_RUNNING); button->ClearState(LauncherButton::STATE_ATTENTION); - button->ClearState(LauncherButton::STATE_PENDING); break; case STATUS_ACTIVE: button->AddState(LauncherButton::STATE_ACTIVE); button->ClearState(LauncherButton::STATE_RUNNING); button->ClearState(LauncherButton::STATE_ATTENTION); - button->ClearState(LauncherButton::STATE_PENDING); break; case STATUS_ATTENTION: button->ClearState(LauncherButton::STATE_ACTIVE); button->ClearState(LauncherButton::STATE_RUNNING); button->AddState(LauncherButton::STATE_ATTENTION); - button->ClearState(LauncherButton::STATE_PENDING); - break; - case STATUS_IS_PENDING: - button->ClearState(LauncherButton::STATE_ACTIVE); - button->ClearState(LauncherButton::STATE_RUNNING); - button->ClearState(LauncherButton::STATE_ATTENTION); - button->AddState(LauncherButton::STATE_PENDING); break; } } @@ -304,6 +287,7 @@ void LauncherView::Init() { AddChildView(child); } UpdateFirstButtonPadding(); + LauncherStatusChanged(); overflow_button_ = new OverflowButton(this); overflow_button_->set_context_menu_controller(this); @@ -522,21 +506,7 @@ views::View* LauncherView::CreateViewForItem(const LauncherItem& item) { case TYPE_APP_LIST: { // TODO(dave): turn this into a LauncherButton too. - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); AppListButton* button = new AppListButton(this, this); - button->SetImage( - views::CustomButton::BS_NORMAL, - rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST).ToImageSkia()); - button->SetImage( - views::CustomButton::BS_HOT, - rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_HOT). - ToImageSkia()); - button->SetImage( - views::CustomButton::BS_PUSHED, - rb.GetImageNamed(IDR_AURA_LAUNCHER_ICON_APPLIST_PUSHED). - ToImageSkia()); - button->SetAccessibleName( - l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE)); view = button; break; } @@ -887,6 +857,15 @@ void LauncherView::LauncherItemMoved(int start_index, int target_index) { AnimateToIdealBounds(); } +void LauncherView::LauncherStatusChanged() { + AppListButton* app_list_button = + static_cast<AppListButton*>(GetAppListButtonView()); + if (model_->status() == LauncherModel::STATUS_LOADING) + app_list_button->StartLoadingAnimation(); + else + app_list_button->StopLoadingAnimation(); +} + void LauncherView::PointerPressedOnButton(views::View* view, Pointer pointer, const ui::LocatedEvent& event) { @@ -979,7 +958,9 @@ string16 LauncherView::GetAccessibleName(const views::View* view) { return delegate_->GetTitle(model_->items()[view_index]); case TYPE_APP_LIST: - return l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE); + return model_->status() == LauncherModel::STATUS_LOADING ? + l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_SYNCING_TITLE) : + l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE); case TYPE_BROWSER_SHORTCUT: return l10n_util::GetStringUTF16(IDS_AURA_NEW_TAB); diff --git a/ash/launcher/launcher_view.h b/ash/launcher/launcher_view.h index bfac558..ca3f632 100644 --- a/ash/launcher/launcher_view.h +++ b/ash/launcher/launcher_view.h @@ -182,6 +182,7 @@ class ASH_EXPORT LauncherView : public views::View, virtual void LauncherItemChanged(int model_index, const ash::LauncherItem& old_item) OVERRIDE; virtual void LauncherItemMoved(int start_index, int target_index) OVERRIDE; + virtual void LauncherStatusChanged() OVERRIDE; // Overridden from LauncherButtonHost: virtual void PointerPressedOnButton( diff --git a/ash/launcher/launcher_view_unittest.cc b/ash/launcher/launcher_view_unittest.cc index d2585bd..5a43c33 100644 --- a/ash/launcher/launcher_view_unittest.cc +++ b/ash/launcher/launcher_view_unittest.cc @@ -536,9 +536,6 @@ TEST_F(LauncherViewTest, LauncherItemStatus) { item.status = ash::STATUS_ATTENTION; model_->Set(index, item); ASSERT_EQ(internal::LauncherButton::STATE_ATTENTION, button->state()); - item.status = ash::STATUS_IS_PENDING; - model_->Set(index, item); - ASSERT_EQ(internal::LauncherButton::STATE_PENDING, button->state()); } // Confirm that item status changes are reflected in the buttons @@ -559,9 +556,6 @@ TEST_F(LauncherViewTest, LauncherItemStatusPlatformApp) { item.status = ash::STATUS_ATTENTION; model_->Set(index, item); ASSERT_EQ(internal::LauncherButton::STATE_ATTENTION, button->state()); - item.status = ash::STATUS_IS_PENDING; - model_->Set(index, item); - ASSERT_EQ(internal::LauncherButton::STATE_PENDING, button->state()); } TEST_F(LauncherViewTest, LauncherTooltipTest) { diff --git a/chrome/browser/extensions/pending_extension_manager.cc b/chrome/browser/extensions/pending_extension_manager.cc index 130f11b..175afa9 100644 --- a/chrome/browser/extensions/pending_extension_manager.cc +++ b/chrome/browser/extensions/pending_extension_manager.cc @@ -4,6 +4,8 @@ #include "chrome/browser/extensions/pending_extension_manager.h" +#include <algorithm> + #include "base/logging.h" #include "base/stl_util.h" #include "base/version.h" @@ -12,8 +14,6 @@ #include "chrome/common/extensions/extension.h" #include "content/public/browser/browser_thread.h" -#include <algorithm> - using content::BrowserThread; namespace { @@ -73,6 +73,18 @@ bool PendingExtensionManager::IsIdPending(const std::string& id) const { return false; } +bool PendingExtensionManager::HasPendingExtensionFromSync() const { + PendingExtensionList::const_iterator iter; + for (iter = pending_extension_list_.begin(); + iter != pending_extension_list_.end(); + ++iter) { + if (iter->is_from_sync()) + return true; + } + + return false; +} + bool PendingExtensionManager::AddFromSync( const std::string& id, const GURL& update_url, diff --git a/chrome/browser/extensions/pending_extension_manager.h b/chrome/browser/extensions/pending_extension_manager.h index 1c8efb8..c9235b7 100644 --- a/chrome/browser/extensions/pending_extension_manager.h +++ b/chrome/browser/extensions/pending_extension_manager.h @@ -56,6 +56,9 @@ class PendingExtensionManager { // Is |id| in the set of pending extensions? bool IsIdPending(const std::string& id) const; + // Whether there is pending extension install from sync. + bool HasPendingExtensionFromSync() const; + // Adds an extension in a pending state; the extension with the // given info will be installed on the next auto-update cycle. // Return true if the extension was added. Will return false diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc index 9ffaeed..6ea3198 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.cc @@ -16,11 +16,14 @@ #include "base/values.h" #include "chrome/browser/defaults.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/pending_extension_manager.h" #include "chrome/browser/prefs/incognito_mode_prefs.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/sync/profile_sync_service_factory.h" #include "chrome/browser/ui/ash/chrome_launcher_prefs.h" #include "chrome/browser/ui/ash/extension_utils.h" #include "chrome/browser/ui/ash/launcher/browser_launcher_item_controller.h" @@ -49,6 +52,13 @@ using extensions::Extension; +namespace { + +// Max loading animation time in milliseconds. +const int kMaxLoadingTimeMs = 60 * 1000; + +} // namespace + // ChromeLauncherController::Item ---------------------------------------------- ChromeLauncherController::Item::Item() @@ -68,12 +78,24 @@ ChromeLauncherController::ChromeLauncherController(Profile* profile, ash::LauncherModel* model) : model_(model), profile_(profile), - activation_client_(NULL) { + activation_client_(NULL), + observed_sync_service_(NULL) { if (!profile_) { // Use the original profile as on chromeos we may get a temporary off the // record profile. profile_ = ProfileManager::GetDefaultProfile()->GetOriginalProfile(); + + // Monitor app sync on chromeos. + if (!IsLoggedInAsGuest()) { + observed_sync_service_ = + ProfileSyncServiceFactory::GetForProfile(profile_); + if (observed_sync_service_) { + observed_sync_service_->AddObserver(this); + StartLoadingAnimation(); + } + } } + instance_ = this; model_->AddObserver(this); extensions::ShellWindowRegistry::Get(profile_)->AddObserver(this); @@ -111,6 +133,9 @@ ChromeLauncherController::~ChromeLauncherController() { if (ash::Shell::HasInstance()) ash::Shell::GetInstance()->RemoveShellObserver(this); + + if (observed_sync_service_) + observed_sync_service_->RemoveObserver(this); } void ChromeLauncherController::Init() { @@ -263,11 +288,6 @@ void ChromeLauncherController::Open(ash::LauncherID id, int event_flags) { ash::wm::ActivateWindow(controller->window()); } else { DCHECK_EQ(TYPE_APP, id_to_item_map_[id].item_type); - - // Do nothing for pending app shortcut. - if (GetItemStatus(id) == ash::STATUS_IS_PENDING) - return; - OpenAppID(id_to_item_map_[id].app_id, event_flags); } } @@ -613,6 +633,9 @@ void ChromeLauncherController::LauncherItemChanged( } } +void ChromeLauncherController::LauncherStatusChanged() { +} + void ChromeLauncherController::Observe( int type, const content::NotificationSource& source, @@ -620,18 +643,15 @@ void ChromeLauncherController::Observe( switch (type) { case chrome::NOTIFICATION_EXTENSION_LOADED: { UpdateAppLaunchersFromPref(); + CheckAppSync(); break; } case chrome::NOTIFICATION_EXTENSION_UNLOADED: { const content::Details<extensions::UnloadedExtensionInfo> unload_info( details); const Extension* extension = unload_info->extension; - if (IsAppPinned(extension->id())) { - if (unload_info->reason == extension_misc::UNLOAD_REASON_UPDATE) - MarkAppPending(extension->id()); - else - DoUnpinAppsWithID(extension->id()); - } + if (IsAppPinned(extension->id())) + DoUnpinAppsWithID(extension->id()); break; } case chrome::NOTIFICATION_PREF_CHANGED: { @@ -744,6 +764,11 @@ void ChromeLauncherController::OnShelfAlignmentChanged() { profile_->GetPrefs()->SetString(prefs::kShelfAlignment, pref_value); } +void ChromeLauncherController::OnStateChanged() { + DCHECK(observed_sync_service_); + CheckAppSync(); +} + void ChromeLauncherController::PersistPinnedState() { // It is a coding error to call PersistPinnedState() if the pinned apps are // not user-editable. The code should check earlier and not perform any @@ -800,18 +825,6 @@ ash::LauncherItemStatus ChromeLauncherController::GetItemStatus( return item.status; } -void ChromeLauncherController::MarkAppPending(const std::string& app_id) { - for (IDToItemMap::const_iterator i = id_to_item_map_.begin(); - i != id_to_item_map_.end(); ++i) { - if (i->second.item_type == TYPE_APP && i->second.app_id == app_id) { - if (GetItemStatus(i->first) == ash::STATUS_CLOSED) - SetItemStatus(i->first, ash::STATUS_IS_PENDING); - - break; - } - } -} - void ChromeLauncherController::DoPinAppWithID(const std::string& app_id) { // If there is an item, do nothing and return. if (IsAppPinned(app_id)) @@ -869,13 +882,6 @@ void ChromeLauncherController::UpdateAppLaunchersFromPref() { IDToItemMap::const_iterator entry(id_to_item_map_.find(item.id)); if (entry != id_to_item_map_.end() && entry->second.app_id == *pref_app_id) { - // Current item will be kept. Reset its pending state and ensure - // its icon is loaded since it has to be valid to be in |pinned_apps|. - if (item.status == ash::STATUS_IS_PENDING) { - SetItemStatus(item.id, ash::STATUS_CLOSED); - app_icon_loader_->FetchImage(*pref_app_id); - } - ++pref_app_id; break; } else { @@ -972,28 +978,55 @@ ash::LauncherID ChromeLauncherController::InsertAppLauncherItem( } item.is_incognito = false; item.image = Extension::GetDefaultIcon(true); - if (item.type == ash::TYPE_APP_SHORTCUT && - !app_tab_helper_->IsValidID(app_id)) { - item.status = ash::STATUS_IS_PENDING; - } else { - TabContents* active_tab = GetLastActiveTabContents(app_id); - if (active_tab) { - Browser* browser = browser::FindBrowserWithWebContents( - active_tab->web_contents()); - DCHECK(browser); - if (browser->window()->IsActive()) - status = ash::STATUS_ACTIVE; - else - status = ash::STATUS_RUNNING; - } - item.status = status; + + TabContents* active_tab = GetLastActiveTabContents(app_id); + if (active_tab) { + Browser* browser = browser::FindBrowserWithWebContents( + active_tab->web_contents()); + DCHECK(browser); + if (browser->window()->IsActive()) + status = ash::STATUS_ACTIVE; + else + status = ash::STATUS_RUNNING; } + item.status = status; + model_->AddAt(index, item); if (!controller || controller->type() != BrowserLauncherItemController::TYPE_EXTENSION_PANEL) { - if (item.status != ash::STATUS_IS_PENDING) - app_icon_loader_->FetchImage(app_id); + app_icon_loader_->FetchImage(app_id); } + return id; } + +void ChromeLauncherController::CheckAppSync() { + if (!observed_sync_service_) + return; + + const bool synced = observed_sync_service_->ShouldPushChanges(); + const bool has_pending_extension = profile_->GetExtensionService()-> + pending_extension_manager()->HasPendingExtensionFromSync(); + + if (synced && !has_pending_extension) + StopLoadingAnimation(); +} + +void ChromeLauncherController::StartLoadingAnimation() { + DCHECK(observed_sync_service_); + loading_timer_.Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(kMaxLoadingTimeMs), + this, &ChromeLauncherController::StopLoadingAnimation); + model_->SetStatus(ash::LauncherModel::STATUS_LOADING); +} + +void ChromeLauncherController::StopLoadingAnimation() { + DCHECK(observed_sync_service_); + + model_->SetStatus(ash::LauncherModel::STATUS_NORMAL); + loading_timer_.Stop(); + observed_sync_service_->RemoveObserver(this); + observed_sync_service_ = NULL; +} diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h index 411a5ea..3a0a841 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller.h @@ -17,9 +17,11 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" +#include "base/timer.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/shell_window_registry.h" #include "chrome/browser/prefs/pref_change_registrar.h" +#include "chrome/browser/sync/profile_sync_service_observer.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" #include "ui/aura/client/activation_change_observer.h" @@ -42,6 +44,7 @@ class BrowserLauncherItemController; class BrowserLauncherItemControllerTest; class PrefService; class Profile; +class ProfileSyncService; class TabContents; // ChromeLauncherController manages the launcher items needed for tabbed @@ -53,7 +56,8 @@ class ChromeLauncherController public content::NotificationObserver, public extensions::ShellWindowRegistry::Observer, public aura::client::ActivationChangeObserver, - public aura::WindowObserver { + public aura::WindowObserver, + public ProfileSyncServiceObserver { public: // Indicates if a launcher item is incognito or not. enum IncognitoState { @@ -226,6 +230,7 @@ class ChromeLauncherController virtual void LauncherItemMoved(int start_index, int target_index) OVERRIDE; virtual void LauncherItemChanged(int index, const ash::LauncherItem& old_item) OVERRIDE; + virtual void LauncherStatusChanged() OVERRIDE; // Overridden from content::NotificationObserver: virtual void Observe(int type, @@ -236,17 +241,20 @@ class ChromeLauncherController virtual void OnShellWindowAdded(ShellWindow* shell_window) OVERRIDE; virtual void OnShellWindowRemoved(ShellWindow* shell_window) OVERRIDE; - // Overriden from client::ActivationChangeObserver: + // Overridden from client::ActivationChangeObserver: virtual void OnWindowActivated( aura::Window* active, aura::Window* old_active) OVERRIDE; - // Overriden from aura::WindowObserver: + // Overridden from aura::WindowObserver: virtual void OnWindowRemovingFromRootWindow(aura::Window* window) OVERRIDE; - // Overriden from ash::ShellObserver: + // Overridden from ash::ShellObserver: virtual void OnShelfAlignmentChanged() OVERRIDE; + // Overridden from ProfileSyncServiceObserver: + virtual void OnStateChanged() OVERRIDE; + private: friend class BrowserLauncherItemControllerTest; friend class ChromeLauncherControllerTest; @@ -290,10 +298,6 @@ class ChromeLauncherController // Returns item status for given |id|. ash::LauncherItemStatus GetItemStatus(ash::LauncherID id) const; - // Finds the launcher item that represents given |app_id| and updates the - // pending state. - void MarkAppPending(const std::string& app_id); - // Internal helpers for pinning and unpinning that handle both // client-triggered and internal pinning operations. void DoPinAppWithID(const std::string& app_id); @@ -319,6 +323,13 @@ class ChromeLauncherController ash::LauncherItemStatus status, int index); + // Checks whether sync is completed and no pending synced extension install + // and calls StopLoadingAnimation when both conditions are met. + void CheckAppSync(); + + void StartLoadingAnimation(); + void StopLoadingAnimation(); + static ChromeLauncherController* instance_; ash::LauncherModel* model_; @@ -354,6 +365,9 @@ class ChromeLauncherController PrefChangeRegistrar pref_change_registrar_; aura::client::ActivationClient* activation_client_; + ProfileSyncService* observed_sync_service_; + base::OneShotTimer<ChromeLauncherController> loading_timer_; + DISALLOW_COPY_AND_ASSIGN(ChromeLauncherController); }; diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc index 784eda0..307a4e7 100644 --- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc +++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc @@ -172,28 +172,6 @@ TEST_F(ChromeLauncherControllerTest, Policy) { EXPECT_FALSE(launcher_controller.IsAppPinned(extension3_->id())); } -TEST_F(ChromeLauncherControllerTest, UnloadAndLoad) { - extension_service_->AddExtension(extension3_.get()); - extension_service_->AddExtension(extension4_.get()); - - ChromeLauncherController launcher_controller(profile_.get(), &model_); - launcher_controller.Init(); - - EXPECT_TRUE(launcher_controller.IsAppPinned(extension3_->id())); - EXPECT_TRUE(launcher_controller.IsAppPinned(extension4_->id())); - - extension_service_->UnloadExtension(extension3_->id(), - extension_misc::UNLOAD_REASON_UPDATE); - EXPECT_TRUE(launcher_controller.IsAppPinned(extension3_->id())); - EXPECT_EQ(ash::STATUS_IS_PENDING, model_.items()[1].status); - - extension_service_->AddExtension(extension3_.get()); - EXPECT_TRUE(launcher_controller.IsAppPinned(extension3_->id())); - EXPECT_EQ(ash::STATUS_CLOSED, model_.items()[1].status); - - EXPECT_TRUE(launcher_controller.IsAppPinned(extension4_->id())); -} - TEST_F(ChromeLauncherControllerTest, UnpinWithUninstall) { extension_service_->AddExtension(extension3_.get()); extension_service_->AddExtension(extension4_.get()); diff --git a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc index 0dfa4b7..4b54777 100644 --- a/chrome/browser/ui/ash/launcher/launcher_context_menu.cc +++ b/chrome/browser/ui/ash/launcher/launcher_context_menu.cc @@ -27,24 +27,21 @@ LauncherContextMenu::LauncherContextMenu(ChromeLauncherController* controller, AddItem( MENU_PIN, l10n_util::GetStringUTF16(IDS_LAUNCHER_CONTEXT_MENU_UNPIN)); - // No open actions for pending app shortcut. - if (item->status != ash::STATUS_IS_PENDING) { - AddSeparator(); - AddCheckItemWithStringId( - LAUNCH_TYPE_REGULAR_TAB, - IDS_APP_CONTEXT_MENU_OPEN_REGULAR); - AddCheckItemWithStringId( - LAUNCH_TYPE_PINNED_TAB, - IDS_APP_CONTEXT_MENU_OPEN_PINNED); - AddCheckItemWithStringId( - LAUNCH_TYPE_WINDOW, - IDS_APP_CONTEXT_MENU_OPEN_WINDOW); - // Even though the launch type is Full Screen it is more accurately - // described as Maximized in Ash. - AddCheckItemWithStringId( - LAUNCH_TYPE_FULLSCREEN, - IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED); - } + AddSeparator(); + AddCheckItemWithStringId( + LAUNCH_TYPE_REGULAR_TAB, + IDS_APP_CONTEXT_MENU_OPEN_REGULAR); + AddCheckItemWithStringId( + LAUNCH_TYPE_PINNED_TAB, + IDS_APP_CONTEXT_MENU_OPEN_PINNED); + AddCheckItemWithStringId( + LAUNCH_TYPE_WINDOW, + IDS_APP_CONTEXT_MENU_OPEN_WINDOW); + // Even though the launch type is Full Screen it is more accurately + // described as Maximized in Ash. + AddCheckItemWithStringId( + LAUNCH_TYPE_FULLSCREEN, + IDS_APP_CONTEXT_MENU_OPEN_MAXIMIZED); } else if (item_.type == ash::TYPE_BROWSER_SHORTCUT) { AddItem(MENU_NEW_WINDOW, l10n_util::GetStringUTF16(IDS_LAUNCHER_NEW_WINDOW)); |