• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
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.latin;
18 
19 import android.content.SharedPreferences;
20 import android.inputmethodservice.InputMethodService;
21 import android.os.Build;
22 import android.os.Handler;
23 import android.os.HandlerThread;
24 import android.os.Process;
25 import android.os.SystemClock;
26 import android.preference.PreferenceManager;
27 import android.text.TextUtils;
28 import android.util.Log;
29 import android.view.MotionEvent;
30 import android.view.inputmethod.CompletionInfo;
31 import android.view.inputmethod.EditorInfo;
32 
33 import com.android.inputmethod.keyboard.Key;
34 import com.android.inputmethod.keyboard.KeyDetector;
35 import com.android.inputmethod.keyboard.Keyboard;
36 import com.android.inputmethod.keyboard.internal.KeyboardState;
37 import com.android.inputmethod.latin.define.ProductionFlag;
38 
39 import java.io.BufferedWriter;
40 import java.io.File;
41 import java.io.FileInputStream;
42 import java.io.FileWriter;
43 import java.io.IOException;
44 import java.io.PrintWriter;
45 import java.nio.ByteBuffer;
46 import java.nio.CharBuffer;
47 import java.nio.channels.FileChannel;
48 import java.nio.charset.Charset;
49 import java.util.Map;
50 
51 /**
52  * Logs the use of the LatinIME keyboard.
53  *
54  * This class logs operations on the IME keyboard, including what the user has typed.
55  * Data is stored locally in a file in app-specific storage.
56  *
57  * This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}.
58  */
59 public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
60     private static final String TAG = ResearchLogger.class.getSimpleName();
61     private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
62     private static final boolean DEBUG = false;
63 
64     private static final ResearchLogger sInstance = new ResearchLogger(new LogFileManager());
65     public static boolean sIsLogging = false;
66     /* package */ final Handler mLoggingHandler;
67     private InputMethodService mIms;
68 
69     /**
70      * Isolates management of files. This variable should never be null, but can be changed
71      * to support testing.
72      */
73     /* package */ LogFileManager mLogFileManager;
74 
75     /**
76      * Manages the file(s) that stores the logs.
77      *
78      * Handles creation, deletion, and provides Readers, Writers, and InputStreams to access
79      * the logs.
80      */
81     /* package */ static class LogFileManager {
82         public static final String RESEARCH_LOG_FILENAME_KEY = "RESEARCH_LOG_FILENAME";
83 
84         private static final String DEFAULT_FILENAME = "researchLog.txt";
85         private static final long LOGFILE_PURGE_INTERVAL = 1000 * 60 * 60 * 24;
86 
87         protected InputMethodService mIms;
88         protected File mFile;
89         protected PrintWriter mPrintWriter;
90 
LogFileManager()91         /* package */ LogFileManager() {
92         }
93 
init(final InputMethodService ims)94         public void init(final InputMethodService ims) {
95             mIms = ims;
96         }
97 
createLogFile()98         public synchronized void createLogFile() throws IOException {
99             createLogFile(DEFAULT_FILENAME);
100         }
101 
createLogFile(final SharedPreferences prefs)102         public synchronized void createLogFile(final SharedPreferences prefs)
103                 throws IOException {
104             final String filename =
105                     prefs.getString(RESEARCH_LOG_FILENAME_KEY, DEFAULT_FILENAME);
106             createLogFile(filename);
107         }
108 
createLogFile(final String filename)109         public synchronized void createLogFile(final String filename)
110                 throws IOException {
111             if (mIms == null) {
112                 final String msg = "InputMethodService is not configured.  Logging is off.";
113                 Log.w(TAG, msg);
114                 throw new IOException(msg);
115             }
116             final File filesDir = mIms.getFilesDir();
117             if (filesDir == null || !filesDir.exists()) {
118                 final String msg = "Storage directory does not exist.  Logging is off.";
119                 Log.w(TAG, msg);
120                 throw new IOException(msg);
121             }
122             close();
123             final File file = new File(filesDir, filename);
124             mFile = file;
125             boolean append = true;
126             if (file.exists() && file.lastModified() + LOGFILE_PURGE_INTERVAL <
127                     System.currentTimeMillis()) {
128                 append = false;
129             }
130             mPrintWriter = new PrintWriter(new BufferedWriter(new FileWriter(file, append)), true);
131         }
132 
append(final String s)133         public synchronized boolean append(final String s) {
134             PrintWriter printWriter = mPrintWriter;
135             if (printWriter == null || !mFile.exists()) {
136                 if (DEBUG) {
137                     Log.w(TAG, "PrintWriter is null... attempting to create default log file");
138                 }
139                 try {
140                     createLogFile();
141                     printWriter = mPrintWriter;
142                 } catch (IOException e) {
143                     Log.w(TAG, "Failed to create log file.  Not logging.");
144                     return false;
145                 }
146             }
147             printWriter.print(s);
148             printWriter.flush();
149             return !printWriter.checkError();
150         }
151 
reset()152         public synchronized void reset() {
153             if (mPrintWriter != null) {
154                 mPrintWriter.close();
155                 mPrintWriter = null;
156                 if (DEBUG) {
157                     Log.d(TAG, "logfile closed");
158                 }
159             }
160             if (mFile != null) {
161                 mFile.delete();
162                 if (DEBUG) {
163                     Log.d(TAG, "logfile deleted");
164                 }
165                 mFile = null;
166             }
167         }
168 
close()169         public synchronized void close() {
170             if (mPrintWriter != null) {
171                 mPrintWriter.close();
172                 mPrintWriter = null;
173                 mFile = null;
174                 if (DEBUG) {
175                     Log.d(TAG, "logfile closed");
176                 }
177             }
178         }
179 
flush()180         /* package */ synchronized void flush() {
181             if (mPrintWriter != null) {
182                 mPrintWriter.flush();
183             }
184         }
185 
getContents()186         /* package */ synchronized String getContents() {
187             final File file = mFile;
188             if (file == null) {
189                 return "";
190             }
191             if (mPrintWriter != null) {
192                 mPrintWriter.flush();
193             }
194             FileInputStream stream = null;
195             FileChannel fileChannel = null;
196             String s = "";
197             try {
198                 stream = new FileInputStream(file);
199                 fileChannel = stream.getChannel();
200                 final ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
201                 fileChannel.read(byteBuffer);
202                 byteBuffer.rewind();
203                 CharBuffer charBuffer = Charset.defaultCharset().decode(byteBuffer);
204                 s = charBuffer.toString();
205             } catch (IOException e) {
206                 e.printStackTrace();
207             } finally {
208                 try {
209                     if (fileChannel != null) {
210                         fileChannel.close();
211                     }
212                 } catch (IOException e) {
213                     e.printStackTrace();
214                 } finally {
215                     try {
216                         if (stream != null) {
217                             stream.close();
218                         }
219                     } catch (IOException e) {
220                         e.printStackTrace();
221                     }
222                 }
223             }
224             return s;
225         }
226     }
227 
ResearchLogger(final LogFileManager logFileManager)228     private ResearchLogger(final LogFileManager logFileManager) {
229         final HandlerThread handlerThread = new HandlerThread("ResearchLogger logging task",
230                 Process.THREAD_PRIORITY_BACKGROUND);
231         handlerThread.start();
232         mLoggingHandler = new Handler(handlerThread.getLooper());
233         mLogFileManager = logFileManager;
234     }
235 
getInstance()236     public static ResearchLogger getInstance() {
237         return sInstance;
238     }
239 
init(final InputMethodService ims, final SharedPreferences prefs)240     public static void init(final InputMethodService ims, final SharedPreferences prefs) {
241         sInstance.initInternal(ims, prefs);
242     }
243 
initInternal(final InputMethodService ims, final SharedPreferences prefs)244     /* package */ void initInternal(final InputMethodService ims, final SharedPreferences prefs) {
245         mIms = ims;
246         final LogFileManager logFileManager = mLogFileManager;
247         if (logFileManager != null) {
248             logFileManager.init(ims);
249             try {
250                 logFileManager.createLogFile(prefs);
251             } catch (IOException e) {
252                 e.printStackTrace();
253             }
254         }
255         if (prefs != null) {
256             sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
257             prefs.registerOnSharedPreferenceChangeListener(this);
258         }
259     }
260 
261     /**
262      * Represents a category of logging events that share the same subfield structure.
263      */
264     private static enum LogGroup {
265         MOTION_EVENT("m"),
266         KEY("k"),
267         CORRECTION("c"),
268         STATE_CHANGE("s"),
269         UNSTRUCTURED("u");
270 
271         private final String mLogString;
272 
LogGroup(final String logString)273         private LogGroup(final String logString) {
274             mLogString = logString;
275         }
276     }
277 
logMotionEvent(final int action, final long eventTime, final int id, final int x, final int y, final float size, final float pressure)278     public void logMotionEvent(final int action, final long eventTime, final int id,
279             final int x, final int y, final float size, final float pressure) {
280         final String eventTag;
281         switch (action) {
282             case MotionEvent.ACTION_CANCEL: eventTag = "[Cancel]"; break;
283             case MotionEvent.ACTION_UP: eventTag = "[Up]"; break;
284             case MotionEvent.ACTION_DOWN: eventTag = "[Down]"; break;
285             case MotionEvent.ACTION_POINTER_UP: eventTag = "[PointerUp]"; break;
286             case MotionEvent.ACTION_POINTER_DOWN: eventTag = "[PointerDown]"; break;
287             case MotionEvent.ACTION_MOVE: eventTag = "[Move]"; break;
288             case MotionEvent.ACTION_OUTSIDE: eventTag = "[Outside]"; break;
289             default: eventTag = "[Action" + action + "]"; break;
290         }
291         if (!TextUtils.isEmpty(eventTag)) {
292             final StringBuilder sb = new StringBuilder();
293             sb.append(eventTag);
294             sb.append('\t'); sb.append(eventTime);
295             sb.append('\t'); sb.append(id);
296             sb.append('\t'); sb.append(x);
297             sb.append('\t'); sb.append(y);
298             sb.append('\t'); sb.append(size);
299             sb.append('\t'); sb.append(pressure);
300             write(LogGroup.MOTION_EVENT, sb.toString());
301         }
302     }
303 
logKeyEvent(final int code, final int x, final int y)304     public void logKeyEvent(final int code, final int x, final int y) {
305         final StringBuilder sb = new StringBuilder();
306         sb.append(Keyboard.printableCode(code));
307         sb.append('\t'); sb.append(x);
308         sb.append('\t'); sb.append(y);
309         write(LogGroup.KEY, sb.toString());
310     }
311 
logCorrection(final String subgroup, final String before, final String after, final int position)312     public void logCorrection(final String subgroup, final String before, final String after,
313             final int position) {
314         final StringBuilder sb = new StringBuilder();
315         sb.append(subgroup);
316         sb.append('\t'); sb.append(before);
317         sb.append('\t'); sb.append(after);
318         sb.append('\t'); sb.append(position);
319         write(LogGroup.CORRECTION, sb.toString());
320     }
321 
logStateChange(final String subgroup, final String details)322     public void logStateChange(final String subgroup, final String details) {
323         write(LogGroup.STATE_CHANGE, subgroup + "\t" + details);
324     }
325 
326     public static class UnsLogGroup {
327         private static final boolean DEFAULT_ENABLED = true;
328 
329         private static final boolean KEYBOARDSTATE_ONCANCELINPUT_ENABLED = DEFAULT_ENABLED;
330         private static final boolean KEYBOARDSTATE_ONCODEINPUT_ENABLED = DEFAULT_ENABLED;
331         private static final boolean KEYBOARDSTATE_ONLONGPRESSTIMEOUT_ENABLED = DEFAULT_ENABLED;
332         private static final boolean KEYBOARDSTATE_ONPRESSKEY_ENABLED = DEFAULT_ENABLED;
333         private static final boolean KEYBOARDSTATE_ONRELEASEKEY_ENABLED = DEFAULT_ENABLED;
334         private static final boolean LATINIME_COMMITCURRENTAUTOCORRECTION_ENABLED = DEFAULT_ENABLED;
335         private static final boolean LATINIME_COMMITTEXT_ENABLED = DEFAULT_ENABLED;
336         private static final boolean LATINIME_DELETESURROUNDINGTEXT_ENABLED = DEFAULT_ENABLED;
337         private static final boolean LATINIME_DOUBLESPACEAUTOPERIOD_ENABLED = DEFAULT_ENABLED;
338         private static final boolean LATINIME_ONDISPLAYCOMPLETIONS_ENABLED = DEFAULT_ENABLED;
339         private static final boolean LATINIME_ONSTARTINPUTVIEWINTERNAL_ENABLED = DEFAULT_ENABLED;
340         private static final boolean LATINIME_ONUPDATESELECTION_ENABLED = DEFAULT_ENABLED;
341         private static final boolean LATINIME_PERFORMEDITORACTION_ENABLED = DEFAULT_ENABLED;
342         private static final boolean LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION_ENABLED
343                 = DEFAULT_ENABLED;
344         private static final boolean LATINIME_PICKPUNCTUATIONSUGGESTION_ENABLED = DEFAULT_ENABLED;
345         private static final boolean LATINIME_PICKSUGGESTIONMANUALLY_ENABLED = DEFAULT_ENABLED;
346         private static final boolean LATINIME_REVERTCOMMIT_ENABLED = DEFAULT_ENABLED;
347         private static final boolean LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT_ENABLED
348                 = DEFAULT_ENABLED;
349         private static final boolean LATINIME_REVERTSWAPPUNCTUATION_ENABLED = DEFAULT_ENABLED;
350         private static final boolean LATINIME_SENDKEYCODEPOINT_ENABLED = DEFAULT_ENABLED;
351         private static final boolean LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT_ENABLED
352                 = DEFAULT_ENABLED;
353         private static final boolean LATINIME_SWITCHTOKEYBOARDVIEW_ENABLED = DEFAULT_ENABLED;
354         private static final boolean LATINKEYBOARDVIEW_ONLONGPRESS_ENABLED = DEFAULT_ENABLED;
355         private static final boolean LATINKEYBOARDVIEW_ONPROCESSMOTIONEVENT_ENABLED
356                 = DEFAULT_ENABLED;
357         private static final boolean LATINKEYBOARDVIEW_SETKEYBOARD_ENABLED = DEFAULT_ENABLED;
358         private static final boolean POINTERTRACKER_CALLLISTENERONCANCELINPUT_ENABLED
359                 = DEFAULT_ENABLED;
360         private static final boolean POINTERTRACKER_CALLLISTENERONCODEINPUT_ENABLED
361                 = DEFAULT_ENABLED;
362         private static final boolean
363                 POINTERTRACKER_CALLLISTENERONPRESSANDCHECKKEYBOARDLAYOUTCHANGE_ENABLED
364                 = DEFAULT_ENABLED;
365         private static final boolean POINTERTRACKER_CALLLISTENERONRELEASE_ENABLED = DEFAULT_ENABLED;
366         private static final boolean POINTERTRACKER_ONDOWNEVENT_ENABLED = DEFAULT_ENABLED;
367         private static final boolean POINTERTRACKER_ONMOVEEVENT_ENABLED = DEFAULT_ENABLED;
368         private static final boolean SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT_ENABLED
369                 = DEFAULT_ENABLED;
370         private static final boolean SUGGESTIONSVIEW_SETSUGGESTIONS_ENABLED = DEFAULT_ENABLED;
371     }
372 
logUnstructured(String logGroup, final String details)373     public static void logUnstructured(String logGroup, final String details) {
374         // TODO: improve performance by making entire class static and/or implementing natively
375         getInstance().write(LogGroup.UNSTRUCTURED, logGroup + "\t" + details);
376     }
377 
write(final LogGroup logGroup, final String log)378     private void write(final LogGroup logGroup, final String log) {
379         // TODO: rewrite in native for better performance
380         mLoggingHandler.post(new Runnable() {
381             @Override
382             public void run() {
383                 final long currentTime = System.currentTimeMillis();
384                 final long upTime = SystemClock.uptimeMillis();
385                 final StringBuilder builder = new StringBuilder();
386                 builder.append(currentTime);
387                 builder.append('\t'); builder.append(upTime);
388                 builder.append('\t'); builder.append(logGroup.mLogString);
389                 builder.append('\t'); builder.append(log);
390                 builder.append('\n');
391                 if (DEBUG) {
392                     Log.d(TAG, "Write: " + '[' + logGroup.mLogString + ']' + log);
393                 }
394                 final String s = builder.toString();
395                 if (mLogFileManager.append(s)) {
396                     // success
397                 } else {
398                     if (DEBUG) {
399                         Log.w(TAG, "Unable to write to log.");
400                     }
401                     // perhaps logfile was deleted.  try to recreate and relog.
402                     try {
403                         mLogFileManager.createLogFile(PreferenceManager
404                                 .getDefaultSharedPreferences(mIms));
405                         mLogFileManager.append(s);
406                     } catch (IOException e) {
407                         e.printStackTrace();
408                     }
409                 }
410             }
411         });
412     }
413 
clearAll()414     public void clearAll() {
415         mLoggingHandler.post(new Runnable() {
416             @Override
417             public void run() {
418                 if (DEBUG) {
419                     Log.d(TAG, "Delete log file.");
420                 }
421                 mLogFileManager.reset();
422             }
423         });
424     }
425 
getLogFileManager()426     /* package */ LogFileManager getLogFileManager() {
427         return mLogFileManager;
428     }
429 
430     @Override
onSharedPreferenceChanged(SharedPreferences prefs, String key)431     public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
432         if (key == null || prefs == null) {
433             return;
434         }
435         sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
436     }
437 
keyboardState_onCancelInput(final boolean isSinglePointer, final KeyboardState keyboardState)438     public static void keyboardState_onCancelInput(final boolean isSinglePointer,
439             final KeyboardState keyboardState) {
440         if (UnsLogGroup.KEYBOARDSTATE_ONCANCELINPUT_ENABLED) {
441             final String s = "onCancelInput: single=" + isSinglePointer + " " + keyboardState;
442             logUnstructured("KeyboardState_onCancelInput", s);
443         }
444     }
445 
keyboardState_onCodeInput( final int code, final boolean isSinglePointer, final int autoCaps, final KeyboardState keyboardState)446     public static void keyboardState_onCodeInput(
447             final int code, final boolean isSinglePointer, final int autoCaps,
448             final KeyboardState keyboardState) {
449         if (UnsLogGroup.KEYBOARDSTATE_ONCODEINPUT_ENABLED) {
450             final String s = "onCodeInput: code=" + Keyboard.printableCode(code)
451                     + " single=" + isSinglePointer
452                     + " autoCaps=" + autoCaps + " " + keyboardState;
453             logUnstructured("KeyboardState_onCodeInput", s);
454         }
455     }
456 
keyboardState_onLongPressTimeout(final int code, final KeyboardState keyboardState)457     public static void keyboardState_onLongPressTimeout(final int code,
458             final KeyboardState keyboardState) {
459         if (UnsLogGroup.KEYBOARDSTATE_ONLONGPRESSTIMEOUT_ENABLED) {
460             final String s = "onLongPressTimeout: code=" + Keyboard.printableCode(code) + " "
461                     + keyboardState;
462             logUnstructured("KeyboardState_onLongPressTimeout", s);
463         }
464     }
465 
keyboardState_onPressKey(final int code, final KeyboardState keyboardState)466     public static void keyboardState_onPressKey(final int code,
467             final KeyboardState keyboardState) {
468         if (UnsLogGroup.KEYBOARDSTATE_ONPRESSKEY_ENABLED) {
469             final String s = "onPressKey: code=" + Keyboard.printableCode(code) + " "
470                     + keyboardState;
471             logUnstructured("KeyboardState_onPressKey", s);
472         }
473     }
474 
keyboardState_onReleaseKey(final KeyboardState keyboardState, final int code, final boolean withSliding)475     public static void keyboardState_onReleaseKey(final KeyboardState keyboardState, final int code,
476             final boolean withSliding) {
477         if (UnsLogGroup.KEYBOARDSTATE_ONRELEASEKEY_ENABLED) {
478             final String s = "onReleaseKey: code=" + Keyboard.printableCode(code)
479                     + " sliding=" + withSliding + " " + keyboardState;
480             logUnstructured("KeyboardState_onReleaseKey", s);
481         }
482     }
483 
latinIME_commitCurrentAutoCorrection(final String typedWord, final String autoCorrection)484     public static void latinIME_commitCurrentAutoCorrection(final String typedWord,
485             final String autoCorrection) {
486         if (UnsLogGroup.LATINIME_COMMITCURRENTAUTOCORRECTION_ENABLED) {
487             if (typedWord.equals(autoCorrection)) {
488                 getInstance().logCorrection("[----]", typedWord, autoCorrection, -1);
489             } else {
490                 getInstance().logCorrection("[Auto]", typedWord, autoCorrection, -1);
491             }
492         }
493     }
494 
latinIME_commitText(final CharSequence typedWord)495     public static void latinIME_commitText(final CharSequence typedWord) {
496         if (UnsLogGroup.LATINIME_COMMITTEXT_ENABLED) {
497             logUnstructured("LatinIME_commitText", typedWord.toString());
498         }
499     }
500 
latinIME_deleteSurroundingText(final int length)501     public static void latinIME_deleteSurroundingText(final int length) {
502         if (UnsLogGroup.LATINIME_DELETESURROUNDINGTEXT_ENABLED) {
503             logUnstructured("LatinIME_deleteSurroundingText", String.valueOf(length));
504         }
505     }
506 
latinIME_doubleSpaceAutoPeriod()507     public static void latinIME_doubleSpaceAutoPeriod() {
508         if (UnsLogGroup.LATINIME_DOUBLESPACEAUTOPERIOD_ENABLED) {
509             logUnstructured("LatinIME_doubleSpaceAutoPeriod", "");
510         }
511     }
512 
latinIME_onDisplayCompletions( final CompletionInfo[] applicationSpecifiedCompletions)513     public static void latinIME_onDisplayCompletions(
514             final CompletionInfo[] applicationSpecifiedCompletions) {
515         if (UnsLogGroup.LATINIME_ONDISPLAYCOMPLETIONS_ENABLED) {
516             final StringBuilder builder = new StringBuilder();
517             builder.append("Received completions:");
518             if (applicationSpecifiedCompletions != null) {
519                 for (int i = 0; i < applicationSpecifiedCompletions.length; i++) {
520                     builder.append("  #");
521                     builder.append(i);
522                     builder.append(": ");
523                     builder.append(applicationSpecifiedCompletions[i]);
524                     builder.append("\n");
525                 }
526             }
527             logUnstructured("LatinIME_onDisplayCompletions", builder.toString());
528         }
529     }
530 
latinIME_onStartInputViewInternal(final EditorInfo editorInfo, final SharedPreferences prefs)531     public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
532             final SharedPreferences prefs) {
533         if (UnsLogGroup.LATINIME_ONSTARTINPUTVIEWINTERNAL_ENABLED) {
534             final StringBuilder builder = new StringBuilder();
535             builder.append("onStartInputView: editorInfo:");
536             builder.append("\tinputType=");
537             builder.append(Integer.toHexString(editorInfo.inputType));
538             builder.append("\timeOptions=");
539             builder.append(Integer.toHexString(editorInfo.imeOptions));
540             builder.append("\tdisplay="); builder.append(Build.DISPLAY);
541             builder.append("\tmodel="); builder.append(Build.MODEL);
542             for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) {
543                 builder.append("\t" + entry.getKey());
544                 Object value = entry.getValue();
545                 builder.append("=" + ((value == null) ? "<null>" : value.toString()));
546             }
547             logUnstructured("LatinIME_onStartInputViewInternal", builder.toString());
548         }
549     }
550 
latinIME_onUpdateSelection(final int lastSelectionStart, final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd, final int newSelStart, final int newSelEnd, final int composingSpanStart, final int composingSpanEnd)551     public static void latinIME_onUpdateSelection(final int lastSelectionStart,
552             final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd,
553             final int newSelStart, final int newSelEnd, final int composingSpanStart,
554             final int composingSpanEnd) {
555         if (UnsLogGroup.LATINIME_ONUPDATESELECTION_ENABLED) {
556             final String s = "onUpdateSelection: oss=" + oldSelStart
557                     + ", ose=" + oldSelEnd
558                     + ", lss=" + lastSelectionStart
559                     + ", lse=" + lastSelectionEnd
560                     + ", nss=" + newSelStart
561                     + ", nse=" + newSelEnd
562                     + ", cs=" + composingSpanStart
563                     + ", ce=" + composingSpanEnd;
564             logUnstructured("LatinIME_onUpdateSelection", s);
565         }
566     }
567 
latinIME_performEditorAction(final int imeActionNext)568     public static void latinIME_performEditorAction(final int imeActionNext) {
569         if (UnsLogGroup.LATINIME_PERFORMEDITORACTION_ENABLED) {
570             logUnstructured("LatinIME_performEditorAction", String.valueOf(imeActionNext));
571         }
572     }
573 
latinIME_pickApplicationSpecifiedCompletion(final int index, final CharSequence text, int x, int y)574     public static void latinIME_pickApplicationSpecifiedCompletion(final int index,
575             final CharSequence text, int x, int y) {
576         if (UnsLogGroup.LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION_ENABLED) {
577             final String s = String.valueOf(index) + '\t' + text + '\t' + x + '\t' + y;
578             logUnstructured("LatinIME_pickApplicationSpecifiedCompletion", s);
579         }
580     }
581 
latinIME_pickSuggestionManually(final String replacedWord, final int index, CharSequence suggestion, int x, int y)582     public static void latinIME_pickSuggestionManually(final String replacedWord,
583             final int index, CharSequence suggestion, int x, int y) {
584         if (UnsLogGroup.LATINIME_PICKSUGGESTIONMANUALLY_ENABLED) {
585             final String s = String.valueOf(index) + '\t' + suggestion + '\t' + x + '\t' + y;
586             logUnstructured("LatinIME_pickSuggestionManually", s);
587         }
588     }
589 
latinIME_punctuationSuggestion(final int index, final CharSequence suggestion, int x, int y)590     public static void latinIME_punctuationSuggestion(final int index,
591             final CharSequence suggestion, int x, int y) {
592         if (UnsLogGroup.LATINIME_PICKPUNCTUATIONSUGGESTION_ENABLED) {
593             final String s = String.valueOf(index) + '\t' + suggestion + '\t' + x + '\t' + y;
594             logUnstructured("LatinIME_pickPunctuationSuggestion", s);
595         }
596     }
597 
latinIME_revertDoubleSpaceWhileInBatchEdit()598     public static void latinIME_revertDoubleSpaceWhileInBatchEdit() {
599         if (UnsLogGroup.LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT_ENABLED) {
600             logUnstructured("LatinIME_revertDoubleSpaceWhileInBatchEdit", "");
601         }
602     }
603 
latinIME_revertSwapPunctuation()604     public static void latinIME_revertSwapPunctuation() {
605         if (UnsLogGroup.LATINIME_REVERTSWAPPUNCTUATION_ENABLED) {
606             logUnstructured("LatinIME_revertSwapPunctuation", "");
607         }
608     }
609 
latinIME_sendKeyCodePoint(final int code)610     public static void latinIME_sendKeyCodePoint(final int code) {
611         if (UnsLogGroup.LATINIME_SENDKEYCODEPOINT_ENABLED) {
612             logUnstructured("LatinIME_sendKeyCodePoint", String.valueOf(code));
613         }
614     }
615 
latinIME_swapSwapperAndSpaceWhileInBatchEdit()616     public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() {
617         if (UnsLogGroup.LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT_ENABLED) {
618             logUnstructured("latinIME_swapSwapperAndSpaceWhileInBatchEdit", "");
619         }
620     }
621 
latinIME_switchToKeyboardView()622     public static void latinIME_switchToKeyboardView() {
623         if (UnsLogGroup.LATINIME_SWITCHTOKEYBOARDVIEW_ENABLED) {
624             final String s = "Switch to keyboard view.";
625             logUnstructured("LatinIME_switchToKeyboardView", s);
626         }
627     }
628 
latinKeyboardView_onLongPress()629     public static void latinKeyboardView_onLongPress() {
630         if (UnsLogGroup.LATINKEYBOARDVIEW_ONLONGPRESS_ENABLED) {
631             final String s = "long press detected";
632             logUnstructured("LatinKeyboardView_onLongPress", s);
633         }
634     }
635 
latinKeyboardView_processMotionEvent(MotionEvent me, int action, long eventTime, int index, int id, int x, int y)636     public static void latinKeyboardView_processMotionEvent(MotionEvent me, int action,
637             long eventTime, int index, int id, int x, int y) {
638         if (UnsLogGroup.LATINKEYBOARDVIEW_ONPROCESSMOTIONEVENT_ENABLED) {
639             final float size = me.getSize(index);
640             final float pressure = me.getPressure(index);
641             if (action != MotionEvent.ACTION_MOVE) {
642                 getInstance().logMotionEvent(action, eventTime, id, x, y, size, pressure);
643             }
644         }
645     }
646 
latinKeyboardView_setKeyboard(final Keyboard keyboard)647     public static void latinKeyboardView_setKeyboard(final Keyboard keyboard) {
648         if (UnsLogGroup.LATINKEYBOARDVIEW_SETKEYBOARD_ENABLED) {
649             StringBuilder builder = new StringBuilder();
650             builder.append("id=");
651             builder.append(keyboard.mId);
652             builder.append("\tw=");
653             builder.append(keyboard.mOccupiedWidth);
654             builder.append("\th=");
655             builder.append(keyboard.mOccupiedHeight);
656             builder.append("\tkeys=[");
657             boolean first = true;
658             for (Key key : keyboard.mKeys) {
659                 if (first) {
660                     first = false;
661                 } else {
662                     builder.append(",");
663                 }
664                 builder.append("{code:");
665                 builder.append(key.mCode);
666                 builder.append(",altCode:");
667                 builder.append(key.mAltCode);
668                 builder.append(",x:");
669                 builder.append(key.mX);
670                 builder.append(",y:");
671                 builder.append(key.mY);
672                 builder.append(",w:");
673                 builder.append(key.mWidth);
674                 builder.append(",h:");
675                 builder.append(key.mHeight);
676                 builder.append("}");
677             }
678             builder.append("]");
679             logUnstructured("LatinKeyboardView_setKeyboard", builder.toString());
680         }
681     }
682 
latinIME_revertCommit(final String originallyTypedWord)683     public static void latinIME_revertCommit(final String originallyTypedWord) {
684         if (UnsLogGroup.LATINIME_REVERTCOMMIT_ENABLED) {
685             logUnstructured("LatinIME_revertCommit", originallyTypedWord);
686         }
687     }
688 
pointerTracker_callListenerOnCancelInput()689     public static void pointerTracker_callListenerOnCancelInput() {
690         final String s = "onCancelInput";
691         if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONCANCELINPUT_ENABLED) {
692             logUnstructured("PointerTracker_callListenerOnCancelInput", s);
693         }
694     }
695 
pointerTracker_callListenerOnCodeInput(final Key key, final int x, final int y, final boolean ignoreModifierKey, final boolean altersCode, final int code)696     public static void pointerTracker_callListenerOnCodeInput(final Key key, final int x,
697             final int y, final boolean ignoreModifierKey, final boolean altersCode,
698             final int code) {
699         if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONCODEINPUT_ENABLED) {
700             final String s = "onCodeInput: " + Keyboard.printableCode(code)
701                     + " text=" + key.mOutputText + " x=" + x + " y=" + y
702                     + " ignoreModifier=" + ignoreModifierKey + " altersCode=" + altersCode
703                     + " enabled=" + key.isEnabled();
704             logUnstructured("PointerTracker_callListenerOnCodeInput", s);
705         }
706     }
707 
pointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange( final Key key, final boolean ignoreModifierKey)708     public static void pointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange(
709             final Key key, final boolean ignoreModifierKey) {
710         if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONPRESSANDCHECKKEYBOARDLAYOUTCHANGE_ENABLED) {
711             final String s = "onPress    : " + KeyDetector.printableCode(key)
712                     + " ignoreModifier=" + ignoreModifierKey
713                     + " enabled=" + key.isEnabled();
714             logUnstructured("PointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange", s);
715         }
716     }
717 
pointerTracker_callListenerOnRelease(final Key key, final int primaryCode, final boolean withSliding, final boolean ignoreModifierKey)718     public static void pointerTracker_callListenerOnRelease(final Key key, final int primaryCode,
719             final boolean withSliding, final boolean ignoreModifierKey) {
720         if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONRELEASE_ENABLED) {
721             final String s = "onRelease  : " + Keyboard.printableCode(primaryCode)
722                     + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey
723                     + " enabled="+ key.isEnabled();
724             logUnstructured("PointerTracker_callListenerOnRelease", s);
725         }
726     }
727 
pointerTracker_onDownEvent(long deltaT, int distanceSquared)728     public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) {
729         if (UnsLogGroup.POINTERTRACKER_ONDOWNEVENT_ENABLED) {
730             final String s = "onDownEvent: ignore potential noise: time=" + deltaT
731                     + " distance=" + distanceSquared;
732             logUnstructured("PointerTracker_onDownEvent", s);
733         }
734     }
735 
pointerTracker_onMoveEvent(final int x, final int y, final int lastX, final int lastY)736     public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX,
737             final int lastY) {
738         if (UnsLogGroup.POINTERTRACKER_ONMOVEEVENT_ENABLED) {
739             final String s = String.format("onMoveEvent: sudden move is translated to "
740                     + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y);
741             logUnstructured("PointerTracker_onMoveEvent", s);
742         }
743     }
744 
suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me)745     public static void suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me) {
746         if (UnsLogGroup.SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT_ENABLED) {
747             final String s = "onTouchEvent: ignore sudden jump " + me;
748             logUnstructured("SuddenJumpingTouchEventHandler_onTouchEvent", s);
749         }
750     }
751 
suggestionsView_setSuggestions(final SuggestedWords mSuggestedWords)752     public static void suggestionsView_setSuggestions(final SuggestedWords mSuggestedWords) {
753         if (UnsLogGroup.SUGGESTIONSVIEW_SETSUGGESTIONS_ENABLED) {
754             logUnstructured("SuggestionsView_setSuggestions", mSuggestedWords.toString());
755         }
756     }
757 }