Home | History | Annotate | Download | only in keyboard
      1 // Copyright (c) 2013 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 "ui/keyboard/keyboard_controller.h"
      6 
      7 #include <set>
      8 
      9 #include "base/bind.h"
     10 #include "base/command_line.h"
     11 #include "content/public/browser/render_widget_host.h"
     12 #include "content/public/browser/render_widget_host_iterator.h"
     13 #include "content/public/browser/render_widget_host_view.h"
     14 #include "ui/aura/window.h"
     15 #include "ui/aura/window_delegate.h"
     16 #include "ui/aura/window_observer.h"
     17 #include "ui/base/cursor/cursor.h"
     18 #include "ui/base/hit_test.h"
     19 #include "ui/base/ime/input_method.h"
     20 #include "ui/base/ime/text_input_client.h"
     21 #include "ui/compositor/layer_animation_observer.h"
     22 #include "ui/compositor/scoped_layer_animation_settings.h"
     23 #include "ui/gfx/path.h"
     24 #include "ui/gfx/rect.h"
     25 #include "ui/gfx/skia_util.h"
     26 #include "ui/keyboard/keyboard_controller_observer.h"
     27 #include "ui/keyboard/keyboard_controller_proxy.h"
     28 #include "ui/keyboard/keyboard_layout_manager.h"
     29 #include "ui/keyboard/keyboard_util.h"
     30 #include "ui/wm/core/masked_window_targeter.h"
     31 
     32 #if defined(OS_CHROMEOS)
     33 #include "base/process/launch.h"
     34 #include "base/sys_info.h"
     35 #endif
     36 
     37 namespace {
     38 
     39 const int kHideKeyboardDelayMs = 100;
     40 
     41 // The virtual keyboard show/hide animation duration.
     42 const int kShowAnimationDurationMs = 350;
     43 const int kHideAnimationDurationMs = 100;
     44 
     45 // The opacity of virtual keyboard container when show animation starts or
     46 // hide animation finishes.
     47 // TODO(rsadam@): Investigate why setting this to zero crashes.
     48 const float kAnimationStartOrAfterHideOpacity = 0.01f;
     49 
     50 // Event targeter for the keyboard container.
     51 class KeyboardContainerTargeter : public wm::MaskedWindowTargeter {
     52  public:
     53   KeyboardContainerTargeter(aura::Window* container,
     54                             keyboard::KeyboardControllerProxy* proxy)
     55       : wm::MaskedWindowTargeter(container),
     56         proxy_(proxy) {
     57   }
     58 
     59   virtual ~KeyboardContainerTargeter() {}
     60 
     61  private:
     62   // wm::MaskedWindowTargeter:
     63   virtual bool GetHitTestMask(aura::Window* window,
     64                               gfx::Path* mask) const OVERRIDE {
     65     if (proxy_ && !proxy_->HasKeyboardWindow())
     66       return true;
     67     gfx::Rect keyboard_bounds = proxy_ ? proxy_->GetKeyboardWindow()->bounds() :
     68         keyboard::DefaultKeyboardBoundsFromWindowBounds(window->bounds());
     69     mask->addRect(RectToSkRect(keyboard_bounds));
     70     return true;
     71   }
     72 
     73   keyboard::KeyboardControllerProxy* proxy_;
     74 
     75   DISALLOW_COPY_AND_ASSIGN(KeyboardContainerTargeter);
     76 };
     77 
     78 // The KeyboardWindowDelegate makes sure the keyboard-window does not get focus.
     79 // This is necessary to make sure that the synthetic key-events reach the target
     80 // window.
     81 // The delegate deletes itself when the window is destroyed.
     82 class KeyboardWindowDelegate : public aura::WindowDelegate {
     83  public:
     84   explicit KeyboardWindowDelegate(keyboard::KeyboardControllerProxy* proxy)
     85       : proxy_(proxy) {}
     86   virtual ~KeyboardWindowDelegate() {}
     87 
     88  private:
     89   // Overridden from aura::WindowDelegate:
     90   virtual gfx::Size GetMinimumSize() const OVERRIDE { return gfx::Size(); }
     91   virtual gfx::Size GetMaximumSize() const OVERRIDE { return gfx::Size(); }
     92   virtual void OnBoundsChanged(const gfx::Rect& old_bounds,
     93                                const gfx::Rect& new_bounds) OVERRIDE {
     94     bounds_ = new_bounds;
     95   }
     96   virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE {
     97     return gfx::kNullCursor;
     98   }
     99   virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE {
    100     return HTNOWHERE;
    101   }
    102   virtual bool ShouldDescendIntoChildForEventHandling(
    103       aura::Window* child,
    104       const gfx::Point& location) OVERRIDE {
    105     return true;
    106   }
    107   virtual bool CanFocus() OVERRIDE { return false; }
    108   virtual void OnCaptureLost() OVERRIDE {}
    109   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {}
    110   virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {}
    111   virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {}
    112   virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE { delete this; }
    113   virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {}
    114   virtual bool HasHitTestMask() const OVERRIDE {
    115     return !proxy_ || proxy_->HasKeyboardWindow();
    116   }
    117   virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE {
    118     if (proxy_ && !proxy_->HasKeyboardWindow())
    119       return;
    120     gfx::Rect keyboard_bounds = proxy_ ? proxy_->GetKeyboardWindow()->bounds() :
    121         keyboard::DefaultKeyboardBoundsFromWindowBounds(bounds_);
    122     mask->addRect(RectToSkRect(keyboard_bounds));
    123   }
    124 
    125   gfx::Rect bounds_;
    126   keyboard::KeyboardControllerProxy* proxy_;
    127 
    128   DISALLOW_COPY_AND_ASSIGN(KeyboardWindowDelegate);
    129 };
    130 
    131 void ToggleTouchEventLogging(bool enable) {
    132 #if defined(OS_CHROMEOS)
    133   if (!base::SysInfo::IsRunningOnChromeOS())
    134     return;
    135   CommandLine command(
    136       base::FilePath("/opt/google/touchscreen/toggle_touch_event_logging"));
    137   if (enable)
    138     command.AppendArg("1");
    139   else
    140     command.AppendArg("0");
    141   VLOG(1) << "Running " << command.GetCommandLineString();
    142   base::LaunchOptions options;
    143   options.wait = true;
    144   base::LaunchProcess(command, options, NULL);
    145 #endif
    146 }
    147 
    148 aura::Window *GetFrameWindow(aura::Window *window) {
    149   // Each container window has a non-negative id.  Stop traversing at the child
    150   // of a container window.
    151   if (!window)
    152     return NULL;
    153   while (window->parent() && window->parent()->id() < 0) {
    154     window = window->parent();
    155   }
    156   return window;
    157 }
    158 
    159 }  // namespace
    160 
    161 namespace keyboard {
    162 
    163 // Observer for both keyboard show and hide animations. It should be owned by
    164 // KeyboardController.
    165 class CallbackAnimationObserver : public ui::LayerAnimationObserver {
    166  public:
    167   CallbackAnimationObserver(ui::LayerAnimator* animator,
    168                             base::Callback<void(void)> callback);
    169   virtual ~CallbackAnimationObserver();
    170 
    171  private:
    172   // Overridden from ui::LayerAnimationObserver:
    173   virtual void OnLayerAnimationEnded(ui::LayerAnimationSequence* seq) OVERRIDE;
    174   virtual void OnLayerAnimationAborted(
    175       ui::LayerAnimationSequence* seq) OVERRIDE;
    176   virtual void OnLayerAnimationScheduled(
    177       ui::LayerAnimationSequence* seq) OVERRIDE {}
    178 
    179   ui::LayerAnimator* animator_;
    180   base::Callback<void(void)> callback_;
    181 
    182   DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver);
    183 };
    184 
    185 CallbackAnimationObserver::CallbackAnimationObserver(
    186     ui::LayerAnimator* animator, base::Callback<void(void)> callback)
    187     : animator_(animator), callback_(callback) {
    188 }
    189 
    190 CallbackAnimationObserver::~CallbackAnimationObserver() {
    191   animator_->RemoveObserver(this);
    192 }
    193 
    194 void CallbackAnimationObserver::OnLayerAnimationEnded(
    195     ui::LayerAnimationSequence* seq) {
    196   if (animator_->is_animating())
    197     return;
    198   animator_->RemoveObserver(this);
    199   callback_.Run();
    200 }
    201 
    202 void CallbackAnimationObserver::OnLayerAnimationAborted(
    203     ui::LayerAnimationSequence* seq) {
    204   animator_->RemoveObserver(this);
    205 }
    206 
    207 class WindowBoundsChangeObserver : public aura::WindowObserver {
    208  public:
    209   virtual void OnWindowBoundsChanged(aura::Window* window,
    210                                      const gfx::Rect& old_bounds,
    211                                      const gfx::Rect& new_bounds) OVERRIDE;
    212   virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
    213 
    214   void AddObservedWindow(aura::Window* window);
    215   void RemoveAllObservedWindows();
    216 
    217  private:
    218   std::set<aura::Window*> observed_windows_;
    219 };
    220 
    221 void WindowBoundsChangeObserver::OnWindowBoundsChanged(aura::Window* window,
    222     const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) {
    223   KeyboardController* controller =  KeyboardController::GetInstance();
    224   if (controller)
    225     controller->UpdateWindowInsets(window);
    226 }
    227 
    228 void WindowBoundsChangeObserver::OnWindowDestroyed(aura::Window* window) {
    229   if (window->HasObserver(this))
    230     window->RemoveObserver(this);
    231   observed_windows_.erase(window);
    232 }
    233 
    234 void WindowBoundsChangeObserver::AddObservedWindow(aura::Window* window) {
    235   if (!window->HasObserver(this)) {
    236     window->AddObserver(this);
    237     observed_windows_.insert(window);
    238   }
    239 }
    240 
    241 void WindowBoundsChangeObserver::RemoveAllObservedWindows() {
    242   for (std::set<aura::Window*>::iterator it = observed_windows_.begin();
    243        it != observed_windows_.end(); ++it)
    244     (*it)->RemoveObserver(this);
    245   observed_windows_.clear();
    246 }
    247 
    248 // static
    249 KeyboardController* KeyboardController::instance_ = NULL;
    250 
    251 KeyboardController::KeyboardController(KeyboardControllerProxy* proxy)
    252     : proxy_(proxy),
    253       input_method_(NULL),
    254       keyboard_visible_(false),
    255       show_on_resize_(false),
    256       lock_keyboard_(false),
    257       type_(ui::TEXT_INPUT_TYPE_NONE),
    258       weak_factory_(this) {
    259   CHECK(proxy);
    260   input_method_ = proxy_->GetInputMethod();
    261   input_method_->AddObserver(this);
    262   window_bounds_observer_.reset(new WindowBoundsChangeObserver());
    263 }
    264 
    265 KeyboardController::~KeyboardController() {
    266   if (container_)
    267     container_->RemoveObserver(this);
    268   if (input_method_)
    269     input_method_->RemoveObserver(this);
    270   ResetWindowInsets();
    271 }
    272 
    273 // static
    274 void KeyboardController::ResetInstance(KeyboardController* controller) {
    275   if (instance_ && instance_ != controller)
    276     delete instance_;
    277   instance_ = controller;
    278 }
    279 
    280 // static
    281 KeyboardController* KeyboardController::GetInstance() {
    282   return instance_;
    283 }
    284 
    285 aura::Window* KeyboardController::GetContainerWindow() {
    286   if (!container_.get()) {
    287     container_.reset(new aura::Window(
    288         new KeyboardWindowDelegate(proxy_.get())));
    289     container_->SetEventTargeter(scoped_ptr<ui::EventTargeter>(
    290         new KeyboardContainerTargeter(container_.get(), proxy_.get())));
    291     container_->SetName("KeyboardContainer");
    292     container_->set_owned_by_parent(false);
    293     container_->Init(aura::WINDOW_LAYER_NOT_DRAWN);
    294     container_->AddObserver(this);
    295     container_->SetLayoutManager(new KeyboardLayoutManager(this));
    296   }
    297   return container_.get();
    298 }
    299 
    300 void KeyboardController::NotifyKeyboardBoundsChanging(
    301     const gfx::Rect& new_bounds) {
    302   current_keyboard_bounds_ = new_bounds;
    303   if (proxy_->HasKeyboardWindow() && proxy_->GetKeyboardWindow()->IsVisible()) {
    304     FOR_EACH_OBSERVER(KeyboardControllerObserver,
    305                       observer_list_,
    306                       OnKeyboardBoundsChanging(new_bounds));
    307     if (keyboard::IsKeyboardOverscrollEnabled()) {
    308       // Adjust the height of the viewport for visible windows on the primary
    309       // display.
    310       // TODO(kevers): Add EnvObserver to properly initialize insets if a
    311       // window is created while the keyboard is visible.
    312       scoped_ptr<content::RenderWidgetHostIterator> widgets(
    313           content::RenderWidgetHost::GetRenderWidgetHosts());
    314       aura::Window *keyboard_window = proxy_->GetKeyboardWindow();
    315       aura::Window *root_window = keyboard_window->GetRootWindow();
    316       while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
    317         content::RenderWidgetHostView* view = widget->GetView();
    318         // Can be NULL, e.g. if the RenderWidget is being destroyed or
    319         // the render process crashed.
    320         if (view) {
    321           aura::Window *window = view->GetNativeView();
    322           // If virtual keyboard failed to load, a widget that displays error
    323           // message will be created and adds as a child of the virtual keyboard
    324           // window. We want to avoid add BoundsChangedObserver to that window.
    325           if (GetFrameWindow(window) != keyboard_window &&
    326               window->GetRootWindow() == root_window) {
    327             gfx::Rect window_bounds = window->GetBoundsInScreen();
    328             gfx::Rect intersect = gfx::IntersectRects(window_bounds,
    329                                                       new_bounds);
    330             int overlap = intersect.height();
    331             if (overlap > 0 && overlap < window_bounds.height())
    332               view->SetInsets(gfx::Insets(0, 0, overlap, 0));
    333             else
    334               view->SetInsets(gfx::Insets());
    335             AddBoundsChangedObserver(window);
    336           }
    337         }
    338       }
    339     } else {
    340       ResetWindowInsets();
    341     }
    342   } else {
    343     current_keyboard_bounds_ = gfx::Rect();
    344   }
    345 }
    346 
    347 void KeyboardController::HideKeyboard(HideReason reason) {
    348   keyboard_visible_ = false;
    349   ToggleTouchEventLogging(true);
    350 
    351   keyboard::LogKeyboardControlEvent(
    352       reason == HIDE_REASON_AUTOMATIC ?
    353           keyboard::KEYBOARD_CONTROL_HIDE_AUTO :
    354           keyboard::KEYBOARD_CONTROL_HIDE_USER);
    355 
    356   NotifyKeyboardBoundsChanging(gfx::Rect());
    357 
    358   set_lock_keyboard(false);
    359 
    360   ui::LayerAnimator* container_animator = container_->layer()->GetAnimator();
    361   animation_observer_.reset(new CallbackAnimationObserver(
    362       container_animator,
    363       base::Bind(&KeyboardController::HideAnimationFinished,
    364                  base::Unretained(this))));
    365   container_animator->AddObserver(animation_observer_.get());
    366 
    367   ui::ScopedLayerAnimationSettings settings(container_animator);
    368   settings.SetTweenType(gfx::Tween::FAST_OUT_LINEAR_IN);
    369   settings.SetTransitionDuration(
    370       base::TimeDelta::FromMilliseconds(kHideAnimationDurationMs));
    371   gfx::Transform transform;
    372   transform.Translate(0, kAnimationDistance);
    373   container_->SetTransform(transform);
    374   container_->layer()->SetOpacity(kAnimationStartOrAfterHideOpacity);
    375 }
    376 
    377 void KeyboardController::AddObserver(KeyboardControllerObserver* observer) {
    378   observer_list_.AddObserver(observer);
    379 }
    380 
    381 void KeyboardController::RemoveObserver(KeyboardControllerObserver* observer) {
    382   observer_list_.RemoveObserver(observer);
    383 }
    384 
    385 void KeyboardController::ShowKeyboard(bool lock) {
    386   set_lock_keyboard(lock);
    387   ShowKeyboardInternal();
    388 }
    389 
    390 void KeyboardController::OnWindowHierarchyChanged(
    391     const HierarchyChangeParams& params) {
    392   if (params.new_parent && params.target == container_.get())
    393     OnTextInputStateChanged(proxy_->GetInputMethod()->GetTextInputClient());
    394 }
    395 
    396 void KeyboardController::Reload() {
    397   if (proxy_->HasKeyboardWindow()) {
    398     // A reload should never try to show virtual keyboard. If keyboard is not
    399     // visible before reload, it should keep invisible after reload.
    400     show_on_resize_ = false;
    401     proxy_->ReloadKeyboardIfNeeded();
    402   }
    403 }
    404 
    405 void KeyboardController::OnTextInputStateChanged(
    406     const ui::TextInputClient* client) {
    407   if (!container_.get())
    408     return;
    409 
    410   type_ = client ? client->GetTextInputType() : ui::TEXT_INPUT_TYPE_NONE;
    411 
    412   if (type_ == ui::TEXT_INPUT_TYPE_NONE && !lock_keyboard_) {
    413     if (keyboard_visible_) {
    414       // Set the visibility state here so that any queries for visibility
    415       // before the timer fires returns the correct future value.
    416       keyboard_visible_ = false;
    417       base::MessageLoop::current()->PostDelayedTask(
    418           FROM_HERE,
    419           base::Bind(&KeyboardController::HideKeyboard,
    420                      weak_factory_.GetWeakPtr(), HIDE_REASON_AUTOMATIC),
    421           base::TimeDelta::FromMilliseconds(kHideKeyboardDelayMs));
    422     }
    423   } else {
    424     // Abort a pending keyboard hide.
    425     if (WillHideKeyboard()) {
    426       weak_factory_.InvalidateWeakPtrs();
    427       keyboard_visible_ = true;
    428     }
    429     proxy_->SetUpdateInputType(type_);
    430     // Do not explicitly show the Virtual keyboard unless it is in the process
    431     // of hiding. Instead, the virtual keyboard is shown in response to a user
    432     // gesture (mouse or touch) that is received while an element has input
    433     // focus. Showing the keyboard requires an explicit call to
    434     // OnShowImeIfNeeded.
    435   }
    436 }
    437 
    438 void KeyboardController::OnInputMethodDestroyed(
    439     const ui::InputMethod* input_method) {
    440   DCHECK_EQ(input_method_, input_method);
    441   input_method_ = NULL;
    442 }
    443 
    444 void KeyboardController::OnShowImeIfNeeded() {
    445   ShowKeyboardInternal();
    446 }
    447 
    448 bool KeyboardController::ShouldEnableInsets(aura::Window* window) {
    449   aura::Window *keyboard_window = proxy_->GetKeyboardWindow();
    450   return (keyboard_window->GetRootWindow() == window->GetRootWindow() &&
    451           keyboard::IsKeyboardOverscrollEnabled() &&
    452           proxy_->GetKeyboardWindow()->IsVisible() &&
    453           keyboard_visible_);
    454 }
    455 
    456 void KeyboardController::UpdateWindowInsets(aura::Window* window) {
    457   aura::Window *keyboard_window = proxy_->GetKeyboardWindow();
    458   if (window == keyboard_window)
    459     return;
    460 
    461   scoped_ptr<content::RenderWidgetHostIterator> widgets(
    462       content::RenderWidgetHost::GetRenderWidgetHosts());
    463   while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
    464     content::RenderWidgetHostView* view = widget->GetView();
    465     if (view && window->Contains(view->GetNativeView())) {
    466       gfx::Rect window_bounds = view->GetNativeView()->GetBoundsInScreen();
    467       gfx::Rect intersect = gfx::IntersectRects(window_bounds,
    468           proxy_->GetKeyboardWindow()->bounds());
    469       int overlap = ShouldEnableInsets(window) ? intersect.height() : 0;
    470       if (overlap > 0 && overlap < window_bounds.height())
    471         view->SetInsets(gfx::Insets(0, 0, overlap, 0));
    472       else
    473         view->SetInsets(gfx::Insets());
    474       return;
    475     }
    476   }
    477 }
    478 
    479 void KeyboardController::ShowKeyboardInternal() {
    480   if (!container_.get())
    481     return;
    482 
    483   if (container_->children().empty()) {
    484     keyboard::MarkKeyboardLoadStarted();
    485     aura::Window* keyboard = proxy_->GetKeyboardWindow();
    486     keyboard->Show();
    487     container_->AddChild(keyboard);
    488     keyboard->set_owned_by_parent(false);
    489   }
    490 
    491   proxy_->ReloadKeyboardIfNeeded();
    492 
    493   if (keyboard_visible_) {
    494     return;
    495   } else if (proxy_->GetKeyboardWindow()->bounds().height() == 0) {
    496     show_on_resize_ = true;
    497     return;
    498   }
    499 
    500   keyboard_visible_ = true;
    501 
    502   // If the controller is in the process of hiding the keyboard, do not log
    503   // the stat here since the keyboard will not actually be shown.
    504   if (!WillHideKeyboard())
    505     keyboard::LogKeyboardControlEvent(keyboard::KEYBOARD_CONTROL_SHOW);
    506 
    507   weak_factory_.InvalidateWeakPtrs();
    508 
    509   // If |container_| has hide animation, its visibility is set to false when
    510   // hide animation finished. So even if the container is visible at this
    511   // point, it may in the process of hiding. We still need to show keyboard
    512   // container in this case.
    513   if (container_->IsVisible() &&
    514       !container_->layer()->GetAnimator()->is_animating())
    515     return;
    516 
    517   ToggleTouchEventLogging(false);
    518   ui::LayerAnimator* container_animator = container_->layer()->GetAnimator();
    519 
    520   // If the container is not animating, makes sure the position and opacity
    521   // are at begin states for animation.
    522   if (!container_animator->is_animating()) {
    523     gfx::Transform transform;
    524     transform.Translate(0, kAnimationDistance);
    525     container_->SetTransform(transform);
    526     container_->layer()->SetOpacity(kAnimationStartOrAfterHideOpacity);
    527   }
    528 
    529   container_animator->set_preemption_strategy(
    530       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    531   animation_observer_.reset(new CallbackAnimationObserver(
    532       container_animator,
    533       base::Bind(&KeyboardController::ShowAnimationFinished,
    534                  base::Unretained(this))));
    535   container_animator->AddObserver(animation_observer_.get());
    536 
    537   proxy_->ShowKeyboardContainer(container_.get());
    538 
    539   {
    540     // Scope the following animation settings as we don't want to animate
    541     // visibility change that triggered by a call to the base class function
    542     // ShowKeyboardContainer with these settings. The container should become
    543     // visible immediately.
    544     ui::ScopedLayerAnimationSettings settings(container_animator);
    545     settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
    546     settings.SetTransitionDuration(
    547         base::TimeDelta::FromMilliseconds(kShowAnimationDurationMs));
    548     container_->SetTransform(gfx::Transform());
    549     container_->layer()->SetOpacity(1.0);
    550   }
    551 }
    552 
    553 void KeyboardController::ResetWindowInsets() {
    554   const gfx::Insets insets;
    555   scoped_ptr<content::RenderWidgetHostIterator> widgets(
    556       content::RenderWidgetHost::GetRenderWidgetHosts());
    557   while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
    558     content::RenderWidgetHostView* view = widget->GetView();
    559     if (view)
    560       view->SetInsets(insets);
    561   }
    562   window_bounds_observer_->RemoveAllObservedWindows();
    563 }
    564 
    565 bool KeyboardController::WillHideKeyboard() const {
    566   return weak_factory_.HasWeakPtrs();
    567 }
    568 
    569 void KeyboardController::ShowAnimationFinished() {
    570   // Notify observers after animation finished to prevent reveal desktop
    571   // background during animation.
    572   NotifyKeyboardBoundsChanging(proxy_->GetKeyboardWindow()->bounds());
    573   proxy_->EnsureCaretInWorkArea();
    574 }
    575 
    576 void KeyboardController::HideAnimationFinished() {
    577   proxy_->HideKeyboardContainer(container_.get());
    578 }
    579 
    580 void KeyboardController::AddBoundsChangedObserver(aura::Window* window) {
    581   aura::Window* target_window = GetFrameWindow(window);
    582   if (target_window)
    583     window_bounds_observer_->AddObservedWindow(target_window);
    584 }
    585 
    586 }  // namespace keyboard
    587