Home | History | Annotate | Download | only in speech
      1 // Copyright (c) 2011 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/speech/speech_input_bubble_controller.h"
      6 
      7 #include "chrome/browser/tab_contents/tab_util.h"
      8 #include "content/browser/browser_thread.h"
      9 #include "content/browser/tab_contents/tab_contents.h"
     10 #include "content/common/notification_registrar.h"
     11 #include "content/common/notification_source.h"
     12 #include "content/common/notification_type.h"
     13 #include "ui/gfx/rect.h"
     14 
     15 namespace speech_input {
     16 
     17 SpeechInputBubbleController::SpeechInputBubbleController(Delegate* delegate)
     18     : delegate_(delegate),
     19       current_bubble_caller_id_(0),
     20       registrar_(new NotificationRegistrar) {
     21 }
     22 
     23 SpeechInputBubbleController::~SpeechInputBubbleController() {
     24   DCHECK(bubbles_.empty());
     25 }
     26 
     27 void SpeechInputBubbleController::CreateBubble(int caller_id,
     28                                                int render_process_id,
     29                                                int render_view_id,
     30                                                const gfx::Rect& element_rect) {
     31   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
     32     BrowserThread::PostTask(
     33         BrowserThread::UI, FROM_HERE,
     34         NewRunnableMethod(this, &SpeechInputBubbleController::CreateBubble,
     35                           caller_id, render_process_id, render_view_id,
     36                           element_rect));
     37     return;
     38   }
     39   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     40   TabContents* tab_contents = tab_util::GetTabContentsByID(render_process_id,
     41                                                            render_view_id);
     42 
     43   DCHECK_EQ(0u, bubbles_.count(caller_id));
     44   SpeechInputBubble* bubble = SpeechInputBubble::Create(tab_contents, this,
     45                                                         element_rect);
     46   if (!bubble)  // could be null if tab or display rect were invalid.
     47     return;
     48 
     49   bubbles_[caller_id] = bubble;
     50 
     51   UpdateTabContentsSubscription(caller_id, BUBBLE_ADDED);
     52 }
     53 
     54 void SpeechInputBubbleController::CloseBubble(int caller_id) {
     55   ProcessRequestInUiThread(caller_id, REQUEST_CLOSE, string16(), 0, 0);
     56 }
     57 
     58 void SpeechInputBubbleController::SetBubbleWarmUpMode(int caller_id) {
     59   ProcessRequestInUiThread(caller_id, REQUEST_SET_WARM_UP_MODE,
     60                            string16(), 0, 0);
     61 }
     62 
     63 void SpeechInputBubbleController::SetBubbleRecordingMode(int caller_id) {
     64   ProcessRequestInUiThread(caller_id, REQUEST_SET_RECORDING_MODE,
     65                            string16(), 0, 0);
     66 }
     67 
     68 void SpeechInputBubbleController::SetBubbleRecognizingMode(int caller_id) {
     69   ProcessRequestInUiThread(caller_id, REQUEST_SET_RECOGNIZING_MODE,
     70                            string16(), 0, 0);
     71 }
     72 
     73 void SpeechInputBubbleController::SetBubbleInputVolume(int caller_id,
     74                                                        float volume,
     75                                                        float noise_volume) {
     76   ProcessRequestInUiThread(caller_id, REQUEST_SET_INPUT_VOLUME, string16(),
     77                            volume, noise_volume);
     78 }
     79 
     80 void SpeechInputBubbleController::SetBubbleMessage(int caller_id,
     81                                                    const string16& text) {
     82   ProcessRequestInUiThread(caller_id, REQUEST_SET_MESSAGE, text, 0, 0);
     83 }
     84 
     85 void SpeechInputBubbleController::UpdateTabContentsSubscription(
     86     int caller_id, ManageSubscriptionAction action) {
     87   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     88 
     89   // If there are any other bubbles existing for the same TabContents, we would
     90   // have subscribed to tab close notifications on their behalf and we need to
     91   // stay registered. So we don't change the subscription in such cases.
     92   TabContents* tab_contents = bubbles_[caller_id]->tab_contents();
     93   for (BubbleCallerIdMap::iterator iter = bubbles_.begin();
     94        iter != bubbles_.end(); ++iter) {
     95     if (iter->second->tab_contents() == tab_contents &&
     96         iter->first != caller_id) {
     97       // At least one other bubble exists for the same TabContents. So don't
     98       // make any change to the subscription.
     99       return;
    100     }
    101   }
    102 
    103   if (action == BUBBLE_ADDED) {
    104     registrar_->Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
    105                     Source<TabContents>(tab_contents));
    106   } else {
    107     registrar_->Remove(this, NotificationType::TAB_CONTENTS_DESTROYED,
    108                     Source<TabContents>(tab_contents));
    109   }
    110 }
    111 
    112 void SpeechInputBubbleController::Observe(NotificationType type,
    113                                           const NotificationSource& source,
    114                                           const NotificationDetails& details) {
    115   if (type == NotificationType::TAB_CONTENTS_DESTROYED) {
    116     // Cancel all bubbles and active recognition sessions for this tab.
    117     TabContents* tab_contents = Source<TabContents>(source).ptr();
    118     BubbleCallerIdMap::iterator iter = bubbles_.begin();
    119     while (iter != bubbles_.end()) {
    120       if (iter->second->tab_contents() == tab_contents) {
    121         BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    122             NewRunnableMethod(
    123                 this,
    124                 &SpeechInputBubbleController::InvokeDelegateButtonClicked,
    125                 iter->first, SpeechInputBubble::BUTTON_CANCEL));
    126         CloseBubble(iter->first);
    127         // We expect to have a very small number of items in this map so
    128         // redo-ing from start is ok.
    129         iter = bubbles_.begin();
    130       } else {
    131         ++iter;
    132       }
    133     }
    134   } else {
    135     NOTREACHED() << "Unknown notification";
    136   }
    137 }
    138 
    139 void SpeechInputBubbleController::ProcessRequestInUiThread(
    140     int caller_id, RequestType type, const string16& text, float volume,
    141     float noise_volume) {
    142   if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
    143     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(
    144         this, &SpeechInputBubbleController::ProcessRequestInUiThread,
    145         caller_id, type, text, volume, noise_volume));
    146     return;
    147   }
    148   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    149   // The bubble may have been closed before we got a chance to process this
    150   // request. So check before proceeding.
    151   if (!bubbles_.count(caller_id))
    152     return;
    153 
    154   bool change_active_bubble = (type == REQUEST_SET_WARM_UP_MODE ||
    155                                type == REQUEST_SET_MESSAGE);
    156   if (change_active_bubble) {
    157     if (current_bubble_caller_id_ && current_bubble_caller_id_ != caller_id)
    158       bubbles_[current_bubble_caller_id_]->Hide();
    159     current_bubble_caller_id_ = caller_id;
    160   }
    161 
    162   SpeechInputBubble* bubble = bubbles_[caller_id];
    163   switch (type) {
    164     case REQUEST_SET_WARM_UP_MODE:
    165       bubble->SetWarmUpMode();
    166       break;
    167     case REQUEST_SET_RECORDING_MODE:
    168       bubble->SetRecordingMode();
    169       break;
    170     case REQUEST_SET_RECOGNIZING_MODE:
    171       bubble->SetRecognizingMode();
    172       break;
    173     case REQUEST_SET_MESSAGE:
    174       bubble->SetMessage(text);
    175       break;
    176     case REQUEST_SET_INPUT_VOLUME:
    177       bubble->SetInputVolume(volume, noise_volume);
    178       break;
    179     case REQUEST_CLOSE:
    180       if (current_bubble_caller_id_ == caller_id)
    181         current_bubble_caller_id_ = 0;
    182       UpdateTabContentsSubscription(caller_id, BUBBLE_REMOVED);
    183       delete bubble;
    184       bubbles_.erase(caller_id);
    185       break;
    186     default:
    187       NOTREACHED();
    188       break;
    189   }
    190 
    191   if (change_active_bubble)
    192     bubble->Show();
    193 }
    194 
    195 void SpeechInputBubbleController::InfoBubbleButtonClicked(
    196     SpeechInputBubble::Button button) {
    197   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    198   DCHECK(current_bubble_caller_id_);
    199 
    200   BrowserThread::PostTask(
    201       BrowserThread::IO, FROM_HERE,
    202       NewRunnableMethod(
    203           this,
    204           &SpeechInputBubbleController::InvokeDelegateButtonClicked,
    205           current_bubble_caller_id_, button));
    206 }
    207 
    208 void SpeechInputBubbleController::InfoBubbleFocusChanged() {
    209   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    210   DCHECK(current_bubble_caller_id_);
    211 
    212   int old_bubble_caller_id = current_bubble_caller_id_;
    213   current_bubble_caller_id_ = 0;
    214 
    215   BrowserThread::PostTask(
    216       BrowserThread::IO, FROM_HERE,
    217       NewRunnableMethod(
    218           this,
    219           &SpeechInputBubbleController::InvokeDelegateFocusChanged,
    220           old_bubble_caller_id));
    221 }
    222 
    223 void SpeechInputBubbleController::InvokeDelegateButtonClicked(
    224     int caller_id, SpeechInputBubble::Button button) {
    225   delegate_->InfoBubbleButtonClicked(caller_id, button);
    226 }
    227 
    228 void SpeechInputBubbleController::InvokeDelegateFocusChanged(int caller_id) {
    229   delegate_->InfoBubbleFocusChanged(caller_id);
    230 }
    231 
    232 }  // namespace speech_input
    233