• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.inputmethod.voice;
18 
19 import com.android.common.speech.LoggingEvents;
20 import com.android.common.userhappiness.UserHappinessSignals;
21 
22 import android.content.Context;
23 import android.content.Intent;
24 
25 /**
26  * Provides the logging facility for voice input events. This fires broadcasts back to
27  * the voice search app which then logs on our behalf.
28  *
29  * Note that debug console logging does not occur in this class. If you want to
30  * see console output of these logging events, there is a boolean switch to turn
31  * on on the VoiceSearch side.
32  */
33 public class VoiceInputLogger {
34     private static final String TAG = VoiceInputLogger.class.getSimpleName();
35 
36     private static VoiceInputLogger sVoiceInputLogger;
37 
38     private final Context mContext;
39 
40     // The base intent used to form all broadcast intents to the logger
41     // in VoiceSearch.
42     private final Intent mBaseIntent;
43 
44     // This flag is used to indicate when there are voice events that
45     // need to be flushed.
46     private boolean mHasLoggingInfo = false;
47 
48     /**
49      * Returns the singleton of the logger.
50      *
51      * @param contextHint a hint context used when creating the logger instance.
52      * Ignored if the singleton instance already exists.
53      */
getLogger(Context contextHint)54     public static synchronized VoiceInputLogger getLogger(Context contextHint) {
55         if (sVoiceInputLogger == null) {
56             sVoiceInputLogger = new VoiceInputLogger(contextHint);
57         }
58         return sVoiceInputLogger;
59     }
60 
VoiceInputLogger(Context context)61     public VoiceInputLogger(Context context) {
62         mContext = context;
63 
64         mBaseIntent = new Intent(LoggingEvents.ACTION_LOG_EVENT);
65         mBaseIntent.putExtra(LoggingEvents.EXTRA_APP_NAME, LoggingEvents.VoiceIme.APP_NAME);
66     }
67 
newLoggingBroadcast(int event)68     private Intent newLoggingBroadcast(int event) {
69         Intent i = new Intent(mBaseIntent);
70         i.putExtra(LoggingEvents.EXTRA_EVENT, event);
71         return i;
72     }
73 
flush()74     public void flush() {
75         if (hasLoggingInfo()) {
76             Intent i = new Intent(mBaseIntent);
77             i.putExtra(LoggingEvents.EXTRA_FLUSH, true);
78             mContext.sendBroadcast(i);
79             setHasLoggingInfo(false);
80         }
81     }
82 
keyboardWarningDialogShown()83     public void keyboardWarningDialogShown() {
84         setHasLoggingInfo(true);
85         mContext.sendBroadcast(newLoggingBroadcast(
86                 LoggingEvents.VoiceIme.KEYBOARD_WARNING_DIALOG_SHOWN));
87     }
88 
keyboardWarningDialogDismissed()89     public void keyboardWarningDialogDismissed() {
90         setHasLoggingInfo(true);
91         mContext.sendBroadcast(newLoggingBroadcast(
92                 LoggingEvents.VoiceIme.KEYBOARD_WARNING_DIALOG_DISMISSED));
93     }
94 
keyboardWarningDialogOk()95     public void keyboardWarningDialogOk() {
96         setHasLoggingInfo(true);
97         mContext.sendBroadcast(newLoggingBroadcast(
98                 LoggingEvents.VoiceIme.KEYBOARD_WARNING_DIALOG_OK));
99     }
100 
keyboardWarningDialogCancel()101     public void keyboardWarningDialogCancel() {
102         setHasLoggingInfo(true);
103         mContext.sendBroadcast(newLoggingBroadcast(
104                 LoggingEvents.VoiceIme.KEYBOARD_WARNING_DIALOG_CANCEL));
105     }
106 
settingsWarningDialogShown()107     public void settingsWarningDialogShown() {
108         setHasLoggingInfo(true);
109         mContext.sendBroadcast(newLoggingBroadcast(
110                 LoggingEvents.VoiceIme.SETTINGS_WARNING_DIALOG_SHOWN));
111     }
112 
settingsWarningDialogDismissed()113     public void settingsWarningDialogDismissed() {
114         setHasLoggingInfo(true);
115         mContext.sendBroadcast(newLoggingBroadcast(
116                 LoggingEvents.VoiceIme.SETTINGS_WARNING_DIALOG_DISMISSED));
117     }
118 
settingsWarningDialogOk()119     public void settingsWarningDialogOk() {
120         setHasLoggingInfo(true);
121         mContext.sendBroadcast(newLoggingBroadcast(
122                 LoggingEvents.VoiceIme.SETTINGS_WARNING_DIALOG_OK));
123     }
124 
settingsWarningDialogCancel()125     public void settingsWarningDialogCancel() {
126         setHasLoggingInfo(true);
127         mContext.sendBroadcast(newLoggingBroadcast(
128                 LoggingEvents.VoiceIme.SETTINGS_WARNING_DIALOG_CANCEL));
129     }
130 
swipeHintDisplayed()131     public void swipeHintDisplayed() {
132         setHasLoggingInfo(true);
133         mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.SWIPE_HINT_DISPLAYED));
134     }
135 
cancelDuringListening()136     public void cancelDuringListening() {
137         setHasLoggingInfo(true);
138         mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.CANCEL_DURING_LISTENING));
139     }
140 
cancelDuringWorking()141     public void cancelDuringWorking() {
142         setHasLoggingInfo(true);
143         mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.CANCEL_DURING_WORKING));
144     }
145 
cancelDuringError()146     public void cancelDuringError() {
147         setHasLoggingInfo(true);
148         mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.CANCEL_DURING_ERROR));
149     }
150 
punctuationHintDisplayed()151     public void punctuationHintDisplayed() {
152         setHasLoggingInfo(true);
153         mContext.sendBroadcast(newLoggingBroadcast(
154                 LoggingEvents.VoiceIme.PUNCTUATION_HINT_DISPLAYED));
155     }
156 
error(int code)157     public void error(int code) {
158         setHasLoggingInfo(true);
159         Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.ERROR);
160         i.putExtra(LoggingEvents.VoiceIme.EXTRA_ERROR_CODE, code);
161         mContext.sendBroadcast(i);
162     }
163 
start(String locale, boolean swipe)164     public void start(String locale, boolean swipe) {
165         setHasLoggingInfo(true);
166         Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.START);
167         i.putExtra(LoggingEvents.VoiceIme.EXTRA_START_LOCALE, locale);
168         i.putExtra(LoggingEvents.VoiceIme.EXTRA_START_SWIPE, swipe);
169         i.putExtra(LoggingEvents.EXTRA_TIMESTAMP, System.currentTimeMillis());
170         mContext.sendBroadcast(i);
171     }
172 
voiceInputDelivered(int length)173     public void voiceInputDelivered(int length) {
174         setHasLoggingInfo(true);
175         Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.VOICE_INPUT_DELIVERED);
176         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, length);
177         mContext.sendBroadcast(i);
178     }
179 
textModifiedByTypingInsertion(int length)180     public void textModifiedByTypingInsertion(int length) {
181         setHasLoggingInfo(true);
182         Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED);
183         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, length);
184         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_TYPE,
185                 LoggingEvents.VoiceIme.TEXT_MODIFIED_TYPE_TYPING_INSERTION);
186         mContext.sendBroadcast(i);
187     }
188 
textModifiedByTypingInsertionPunctuation(int length)189     public void textModifiedByTypingInsertionPunctuation(int length) {
190         setHasLoggingInfo(true);
191         Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED);
192         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, length);
193         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_TYPE,
194                 LoggingEvents.VoiceIme.TEXT_MODIFIED_TYPE_TYPING_INSERTION_PUNCTUATION);
195         mContext.sendBroadcast(i);
196     }
197 
textModifiedByTypingDeletion(int length)198     public void textModifiedByTypingDeletion(int length) {
199         setHasLoggingInfo(true);
200         Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED);
201         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, length);
202         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_TYPE,
203                 LoggingEvents.VoiceIme.TEXT_MODIFIED_TYPE_TYPING_DELETION);
204 
205         mContext.sendBroadcast(i);
206     }
207 
textModifiedByChooseSuggestion(int suggestionLength, int replacedPhraseLength, int index, String before, String after)208     public void textModifiedByChooseSuggestion(int suggestionLength, int replacedPhraseLength,
209                                                int index, String before, String after) {
210         setHasLoggingInfo(true);
211         Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED);
212         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_TYPE,
213                    LoggingEvents.VoiceIme.TEXT_MODIFIED_TYPE_CHOOSE_SUGGESTION);
214         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_MODIFIED_LENGTH, suggestionLength);
215         i.putExtra(LoggingEvents.VoiceIme.EXTRA_TEXT_REPLACED_LENGTH, replacedPhraseLength);
216         i.putExtra(LoggingEvents.VoiceIme.EXTRA_N_BEST_CHOOSE_INDEX, index);
217         i.putExtra(LoggingEvents.VoiceIme.EXTRA_BEFORE_N_BEST_CHOOSE, before);
218         i.putExtra(LoggingEvents.VoiceIme.EXTRA_AFTER_N_BEST_CHOOSE, after);
219         mContext.sendBroadcast(i);
220     }
221 
inputEnded()222     public void inputEnded() {
223         setHasLoggingInfo(true);
224         mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.INPUT_ENDED));
225     }
226 
voiceInputSettingEnabled()227     public void voiceInputSettingEnabled() {
228         setHasLoggingInfo(true);
229         mContext.sendBroadcast(newLoggingBroadcast(
230                 LoggingEvents.VoiceIme.VOICE_INPUT_SETTING_ENABLED));
231     }
232 
voiceInputSettingDisabled()233     public void voiceInputSettingDisabled() {
234         setHasLoggingInfo(true);
235         mContext.sendBroadcast(newLoggingBroadcast(
236                 LoggingEvents.VoiceIme.VOICE_INPUT_SETTING_DISABLED));
237     }
238 
setHasLoggingInfo(boolean hasLoggingInfo)239     private void setHasLoggingInfo(boolean hasLoggingInfo) {
240         mHasLoggingInfo = hasLoggingInfo;
241         // If applications that call UserHappinessSignals.userAcceptedImeText
242         // make that call after VoiceInputLogger.flush() calls this method with false, we
243         // will lose those happiness signals. For example, consider the gmail sequence:
244         // 1. compose message
245         // 2. speak message into message field
246         // 3. type subject into subject field
247         // 4. press send
248         // We will NOT get the signal that the user accepted the voice inputted message text
249         // because when the user tapped on the subject field, the ime's flush will be triggered
250         // and the hasLoggingInfo will be then set to false. So by the time the user hits send
251         // we have essentially forgotten about any voice input.
252         // However the following (more common) use case is properly logged
253         // 1. compose message
254         // 2. type subject in subject field
255         // 3. speak message in message field
256         // 4. press send
257         UserHappinessSignals.setHasVoiceLoggingInfo(hasLoggingInfo);
258     }
259 
hasLoggingInfo()260     private boolean hasLoggingInfo(){
261         return mHasLoggingInfo;
262     }
263 
264 }
265