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 91 /* package */ LogFileManager() { 92 } 93 94 public void init(final InputMethodService ims) { 95 mIms = ims; 96 } 97 98 public synchronized void createLogFile() throws IOException { 99 createLogFile(DEFAULT_FILENAME); 100 } 101 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 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 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 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 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 180 /* package */ synchronized void flush() { 181 if (mPrintWriter != null) { 182 mPrintWriter.flush(); 183 } 184 } 185 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 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 236 public static ResearchLogger getInstance() { 237 return sInstance; 238 } 239 240 public static void init(final InputMethodService ims, final SharedPreferences prefs) { 241 sInstance.initInternal(ims, prefs); 242 } 243 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 273 private LogGroup(final String logString) { 274 mLogString = logString; 275 } 276 } 277 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 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 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 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 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 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 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 426 /* package */ LogFileManager getLogFileManager() { 427 return mLogFileManager; 428 } 429 430 @Override 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 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 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 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 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 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 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 495 public static void latinIME_commitText(final CharSequence typedWord) { 496 if (UnsLogGroup.LATINIME_COMMITTEXT_ENABLED) { 497 logUnstructured("LatinIME_commitText", typedWord.toString()); 498 } 499 } 500 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 507 public static void latinIME_doubleSpaceAutoPeriod() { 508 if (UnsLogGroup.LATINIME_DOUBLESPACEAUTOPERIOD_ENABLED) { 509 logUnstructured("LatinIME_doubleSpaceAutoPeriod", ""); 510 } 511 } 512 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 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 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 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 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 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 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 598 public static void latinIME_revertDoubleSpaceWhileInBatchEdit() { 599 if (UnsLogGroup.LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT_ENABLED) { 600 logUnstructured("LatinIME_revertDoubleSpaceWhileInBatchEdit", ""); 601 } 602 } 603 604 public static void latinIME_revertSwapPunctuation() { 605 if (UnsLogGroup.LATINIME_REVERTSWAPPUNCTUATION_ENABLED) { 606 logUnstructured("LatinIME_revertSwapPunctuation", ""); 607 } 608 } 609 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 616 public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() { 617 if (UnsLogGroup.LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT_ENABLED) { 618 logUnstructured("latinIME_swapSwapperAndSpaceWhileInBatchEdit", ""); 619 } 620 } 621 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 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 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 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 683 public static void latinIME_revertCommit(final String originallyTypedWord) { 684 if (UnsLogGroup.LATINIME_REVERTCOMMIT_ENABLED) { 685 logUnstructured("LatinIME_revertCommit", originallyTypedWord); 686 } 687 } 688 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 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 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 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 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 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 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 752 public static void suggestionsView_setSuggestions(final SuggestedWords mSuggestedWords) { 753 if (UnsLogGroup.SUGGESTIONSVIEW_SETSUGGESTIONS_ENABLED) { 754 logUnstructured("SuggestionsView_setSuggestions", mSuggestedWords.toString()); 755 } 756 } 757 }