Merge "[Rlog79b] Save channel name during internal use"
diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml
index 20484fc..6de0e0b 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Кнопка пераключэння мовы звязана i з iншымi спосабамi ўводу"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Кнопка пераключэння мовы"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Паказваць, калі ўключана некалькі моў ўводу"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Iндыкатар слайд-шоу"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Паказаць візуальны сігнал падчас слiзгання клавiш Shift або Symbol"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Затрым. скр. падк. клав."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Няма затрымкі"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Па змаўчанні"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index ebf9dac..c971d7b 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Клавишът за превкл. на езика обхваща и други методи за въвеждане"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Клавиш за превкл. на езика"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Показване при няколко активирани езика за въвеждане"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Индикатор за плъзгане: Показв."</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Визуална подсказка при плъзгане от „Shift“ или клавиш за символи"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Отхвърляне на подсказката"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без задържане"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По подразбиране"</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 4ebc910..23dcda9 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La tecla de canvi d\'idioma serveix també per a altres mètodes d\'entrada"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla de canvi d\'idioma"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostra-la quan hi hagi diversos idiomes d\'entrada activats"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Mostra l\'indicador per lliscar"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Mostra la indicació visual en lliscar des de la tecla Maj o Símbol"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retard en ampliar tecla"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sense retard"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminat"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index f34414b..5625c74 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Klávesa pro přepínání jazyka ovládá i další metody zadávání"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Klávesa přepínání jazyka"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Zobrazit, když je aktivováno více vstupních jazyků"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Indikátor přejetí prstem"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Zobrazit vizuální signál při přejetí z klávesy Shift nebo Symbol"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Prodleva vysk. okna klávesnice"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez prodlevy"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Výchozí"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 9ff0264..6c7936f 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tasten til sprogskift gælder også for andre inputmetoder"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Tast til sprogskift"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Vis, når der er aktiveret flere inputsprog"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Vis indikator ved glidning"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Vis et visuelt tip, når du glider fra Shift eller symboltaster"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Forsink. afvis. af taste-pop op"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Ingen forsink."</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 478e369..18fb51b 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Sprachwechseltaste umfasst auch andere Eingabemethoden."</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Sprachwechsel"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Anzeigen, wenn mehrere Eingabesprachen aktiviert sind"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Ziehbewegung anzeigen"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Ziehen mit gedrückter Shift- oder Symboltaste visuell darstellen"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tasten-Pop-up"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Keine Verzögerung"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 7775cbe..51a6dcc 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La tecla de cambio de idioma abarca otros métodos de entrada."</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla de selección de idioma"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostrar cuando se habiliten varios idiomas de entrada"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Mostrar indicador p. deslizar"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Mostrar indic. visual mientras se desliza de tecl. símb. o mayús"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso en rechazo de alerta de tecla"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin demora"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminada"</string>
diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml
index 29d80ee..9cf6a9f 100644
--- a/java/res/values-et/strings.xml
+++ b/java/res/values-et/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Keelevahetuse võti hõlmab ka muid sisestusmeetodeid"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Keelevahetuse nupp"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Kuva, kui lubatud on mitu sisendkeelt"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Kuva lohistamisnäidik"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Tõstu- või sümboliklahvidelt lohistades anna visuaalselt märku"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Hüpiku loobumisviivitus"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Viivituseta"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Vaikeseade"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index a6676e0..6821058 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"کلید تغییر زبان، سایر ورودی‌های زبان را نیز پوشش می‌دهد"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"کلید تغییر زبان"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"وقتی چند زبان ورودی فعال است نشان داده شود"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"نمایش نشانگر لغزاندن"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"نمایش نشان بصری، هنگام لغزاندن کلیدهای Shift یا نماد"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"تأخیر در رد کردن کلید نمایشی"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"بدون تأخیر"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"پیش‌فرض"</string>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index c085f9a..4e55232 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Kielenvaihtonäppäin kattaa myös muut syöttötavat"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Kielenvaihtonäppäin"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Näytä, kun käytössä on useita syöttökieliä"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Näytä liu\'utuksen tilaosoitin"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Näytä visuaalinen vihje liu\'uttaessasi Shift- tai Symbol-näppäim."</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Näppäimen hylkäysviive"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Ei viivettä"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Oletus"</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index d595224..f20cd2d 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La touche de sélection de langue couvre d\'autres modes de saisie."</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Touche de sélection de langue"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Afficher lorsque plusieurs langues de saisie sont activées"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Aff. indicateur saisie gest."</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Aff. un repère visuel si l\'utilisateur appuie sur Maj ou Symboles"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Masquer touche agrandie"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sans délai"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Par défaut"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index bd725ab..72320ea 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"भाषा स्‍विच कुंजी में अन्‍य इनपुट पद्धतियां भी शामिल हैं"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"भाषा स्विच कुंजी"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"एकाधिक इनपुट भाषाएं सक्षम होने पर दिखाएं"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"स्लाइड संकेतक दिखाएं"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Shift या Symbol कुंजियो से स्लाइड करते समय विज़ुअल क्यू दिखाएं"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"कुंजी पॉपअप खारिज़ विलंब"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"कोई विलंब नहीं"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"डिफ़ॉल्ट"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index 23a3dfa..78fba96 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tipka za prebacivanje jezika pokriva i druge načine unosa"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Tipka za izmjenjivanje jezika"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Prikaži kada je omogućen unos na više jezika"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Prikaži pokazivač klizanja"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Prikaži vizualni znak tijekom klizanja od tipki Shift ili Simbol"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Odgoda prikaza tipki"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez odgode"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Zadano"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 296741a..eb4bde2 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"A nyelvkapcsoló gomb egyéb beviteli módokat is tartalmaz"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"A nyelvkapcsoló"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Megjelenítés, ha több beviteli nyelv engedélyezett"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Diajelző megjelenítése"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Vizuális jelzés Shift vagy Szimbólumok billentyűről csúsztatásnál"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Gombeltüntetés késése"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nincs késés"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Alapbeállítás"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index 712e194..646c3aa 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tombol beralih bahasa juga mencakup metode masukan lain"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Tombol pengalih bahasa"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Tampilkan saat beberapa bahasa masukan diaktifkan"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Tampilkan indikator geser"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Tampilkan isyarat visual saat menggeser dari tombol Shift/Simbol"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tundaan singkir munculan kunci"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Tanpa penundaan"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 8ff95c7..a5d16c6 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Il tasto per cambiare lingua offre altri metodi di immissione"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Tasto cambio lingua"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostra quando sono attive più lingue di immissione"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Mostra indicatore scorrimento"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Visualizza indicatore durante scorrimento da Maiusc o Simbolo"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ritardo eliminaz. popup tasto"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nessun ritardo"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinito"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 7700bb6..2c6a5e3 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"言語切り替えキーは他の入力方法にも対応しています"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"言語切り替えキー"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"複数の入力言語が有効なときに表示"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"スライドインジケーターを表示"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Shiftまたは記号キーからスライドするとビジュアルキューを表示します"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"キーのポップアップ時間"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"すぐに消去"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"デフォルト"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index cb2561b..13fb4cf 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"언어 전환 키가 제공하는 기타 입력 방법"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"언어 전환 키"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"여러 입력 언어를 사용하도록 설정할 때 표시"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"슬라이드 표시기 표시"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Shift 또는 기호 키에서 슬라이드하는 동안 시각 효과 표시"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"키 팝업 해제 지연"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"지연 없음"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"기본값"</string>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index b25379a..b023a17 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Kalbos perjungimo klavišu taip pat perjungiami įvesties būdai"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Kalbos keitimo klavišas"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Rodyti, kai įgalintos kelios įvesties kalbos"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Rodyti šliaužiklio indikatorių"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Rodyti vaizd. užuominą slenkant nuo „Shift“ ar simb. klavišų"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Pagr. išš. l. atsis. d."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Be delsos"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Numatytasis"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index 31d0fe6..23be520 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Valodas pārslēgš. taustiņu var lietot arī citām ievades metodēm."</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Valodas pārslēgšanas taustiņš"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Parādīt, ja ir iespējotas vairākas ievades valodas"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Rādīt vilkšanas indikatoru"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Attēlot vizuālu norādījumu, velkot no Shift vai simbolu taustiņa"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Taust. uzn. loga noraid. aizk."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez aizkaves"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Noklusējums"</string>
diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml
index 6839f10..37f923c 100644
--- a/java/res/values-ms/strings.xml
+++ b/java/res/values-ms/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Kunci pertukaran bahasa meliputi kaedah masukan lain juga"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Kekunci tukar bahasa"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Tunjukkan apabila berbilang bahasa input didayakan"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Tunjukkan penunjuk slaid"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Paparkan petunjuk visual semasa meluncur daripada kekunci Shift atau Simbol"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Pop tmbl knci ketpkn lengah"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Tiada lengah"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Lalai"</string>
@@ -150,7 +148,7 @@
     <string name="not_now" msgid="6172462888202790482">"Bukan sekarang"</string>
     <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Gaya input yang sama sudah wujud: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
     <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mod kajian kebolehgunaan"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="1881822418815012326">"Tetapan kelewatan tekan lama kekunci"</string>
+    <string name="prefs_key_longpress_timeout_settings" msgid="1881822418815012326">"Tetapan kelengahan tekan lama kekunci"</string>
     <string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Tetapan tempoh getaran tekan kekunci"</string>
     <string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Tetapan kelantangan bunyi tekanan kekunci"</string>
     <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Baca fail kamus luaran"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 72d5719..b79933f 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tasten for språkbytte dekker også andre inndatametoder"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Nøkkel for språkskifte"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Vis når flere inndataspråk er aktivert"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Vis lysbildeindikator"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Bruk en visuell indikator når du skyver fra Shift- eller symboltaster"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tregt tastevindu"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"U/ forsinkelse"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 0a8c597..4ff7842 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Schakelknop voor taal ook van toepassing op andere invoermethoden"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Schakelknop voor taal"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Weergeven wanneer meerdere invoertalen zijn geselecteerd"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Schuifaanduiding weergeven"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Zichtbare aanduiding weergeven wanneer wordt geschoven met Shift- of symbooltoetsen"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Afwijz.vertr. toetspop-up"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Geen vertraging"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standaard"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index f05a594..11602b8 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Klawisz zmiany języka obejmuje też inne metody wprowadzania"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Klawisz zmiany języka"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Pokaż, gdy włączonych jest kilka języków wprowadzania"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Pokaż wskaźnik przesuwania"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Wyświetl ślad przesuwania od klawiszy Shift lub Symbol"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Opóźnienie znikania klawiszy"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez opóźnienia"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Wartość domyślna"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 284cb3e..33f5eca 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"A tecla p/ mudar o idioma também cobre outros métodos de entrada"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla de seleção de idioma"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostrar quando vários idiomas de entrada estiverem ativados"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Mostrar indicador de deslize"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Mostrar indicação visual ao deslizar teclas Shift ou de símbolos"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Dispens. atraso chave princ."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sem atraso"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Padrão"</string>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 0797ac2..bb80542 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tasta de comutare între limbi include şi alte metode de introd."</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Tastă comutare limbi"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Afişaţi când sunt activate mai multe limbi de intrare"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Afișați indicator glisare"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Afișați sugestie vizuală la glisarea de la Shift sau Simbol"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Înt. înch. pop-up esenţ."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Fără întârziere"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Prestabilit"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 7139b0d..5e746bb 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Клавиша переключения языков также служит для смены способа ввода"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Клавиша смены языка"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Показывать, когда включено несколько раскладок"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Показывать индикатор перехода"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Индикатор перехода между регистрами или цифр. и букв. режимами"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Задержка закрытия"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без задержки"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По умолчанию"</string>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index e6532d3..9fd9798 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Kláves na prepnutie jazyka pokrýva aj ďalšie metódy vstupu"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Kľúč na prepínanie jazyka"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Zobraziť, keď je povolených viac jazykov vstupu"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Zobraziť posuvný indikátor"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Zobraziť vizuálny signál pri prejdení z klávesov Shift alebo Symbol"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Onesk. zrušenia kľúč. kon. okna"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez oneskorenia"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predvolená"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 20e34e8..fa441d7 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tipka za preklop jezika, ki vključuje tudi druge načine vnosa"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Tipka za preklop med jeziki"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Pokaži, ko je omogočenih več jezikov vnosa"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Pokaži indikator drsenja"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Pokaži vizualni znak pri drsenju s tipk Shift ali Symbol"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Trajanje povečanja tipke"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Brez zakasnitve"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Privzeto"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 55d0370..0a97437 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Тастер за пребацивање језика обухвата и друге методе уноса"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Тастер за пребацивање језика"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Прикажи када је омогућено више језика уноса"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Прикажи индикатор превлачења"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Приказује визуел. траг при превлачењу са тастера Shift или Symbol"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Одложи одбац. иск. прозора тастера"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без одлагања"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Подразумевано"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 720475a..dd8af36 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -40,7 +40,7 @@
     <string name="show_language_switch_key" msgid="5915478828318774384">"Knapp för att byta språk"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Visa när flera inmatningsspråk är aktiverade"</string>
     <string name="sliding_key_input_preview" msgid="6604262359510068370">"Visa dragreglage"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Visa en visuell kö när du drar från Skift- eller Symboltangenten"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Visa svepning när du drar från Skift- eller Symboltangenten"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ta bort popup-fördröjning"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Fördröj inte"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index d28afdd..008e3e2 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"แป้นสลับภาษาครอบคลุมวิธีการป้อนข้อมูลอื่นๆ ด้วย"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"แป้นสลับภาษา"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"แสดงเมื่อเปิดใช้งานภาษาสำหรับอินพุตหลายภาษา"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"แสดงสัญลักษณ์การเลื่อน"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"แสดงสัญลักษณ์ภาพขณะเลื่อนจากแป้น Shift หรือแป้นสัญลักษณ์"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"หน่วงเวลาก่อนปิดป๊อปอัพหลัก"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ไม่มีการหน่วงเวลา"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ค่าเริ่มต้น"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index c16db4c..ae29e6a 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Saklaw din ng key ng pagpalit ng wika ang ibang paraan ng input"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Key ng panlipat ng wika"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Ipakita kapag maraming wika ng input na pinagana"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Ipakita ang indicator ng slide"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Magpakita ng visual cue kapag nag-i-slide sa Shift o Symbol key"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Balewala antala key popup"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Walang antala"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index 1d145a1..eb8f2dc 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Dil geçiş tuşu diğer giriş yöntemlerini de kapsar"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Dil değiştirme tuşu"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Birden fazla giriş dili etkin olduğunda göster"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Kaydırma göstergesini aç"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Üst Karakter veya Sembol tuşlarından kaydırırken görsel ipucu ver"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tuş popup\'ının kapatılmasını geciktirme"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Gecikme yok"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Varsayılan"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index d571dd4..5eaad63 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -39,8 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Клавіша зміни мови дозволяє змінювати методи введення"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Клавіша зміни мови"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Показувати, коли ввімкнено декілька мов введення"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Показати індикатор проведення"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Показувати сигнал під час проведення від клавіш символів чи Shift"</string>
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Показати індикатор переходу"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Показувати сигнал під час переходу від клавіш символів чи Shift"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Затримка клавіши закриття"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без затримки"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"За умовчанням"</string>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 5755eea..fd778d5 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Khóa chuyển ngôn ngữ bao gồm cả các phương thức nhập liệu khác"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"Phím chuyển đổi ngôn ngữ"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Hiển thị khi nhiều ngôn ngữ đầu vào được bật"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Hiển thị chỉ báo trượt"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Hiển thị tín hiệu hình ảnh trong khi trượt từ phím Shift hoặc phím Biểu tượng"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Loại bỏ hiển thị phím trễ"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Không có tgian trễ"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Mặc định"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index cb74c67..6ee06d8 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"语言切换键也可用于切换其他输入法"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"语言切换键"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"启用了多种输入语言时显示"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"显示滑动指示器"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"从 Shift 键或符号键滑动时显示视觉提示"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"弹出字符隐藏延迟"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"无延迟"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"默认"</string>
@@ -125,19 +123,19 @@
     <string name="prefs_enable_log" msgid="6620424505072963557">"启用用户反馈"</string>
     <string name="prefs_description_log" msgid="5827825607258246003">"自动向 Google 发送使用情况统计信息和崩溃报告,帮助改进该输入法编辑器。"</string>
     <string name="keyboard_layout" msgid="8451164783510487501">"键盘主题"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"英语(英国)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"英语(美国)"</string>
+    <string name="subtype_en_GB" msgid="88170601942311355">"英语(英国)"</string>
+    <string name="subtype_en_US" msgid="6160452336634534239">"英语(美国)"</string>
     <string name="subtype_es_US" msgid="5583145191430180200">"西班牙语(美国)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"英语(英国)(<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"英语(美国)(<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"英语(英国)(<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"英语(美国)(<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"西班牙语(美国)(<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_no_language" msgid="141420857808801746">"无语言"</string>
-    <string name="subtype_no_language_qwerty" msgid="2956121451616633133">"无语言(QWERTY 键盘)"</string>
-    <string name="subtype_no_language_qwertz" msgid="1177848172397202890">"无语言(QWERTZ 键盘)"</string>
-    <string name="subtype_no_language_azerty" msgid="8721460968141187394">"无语言(AZERTY 键盘)"</string>
-    <string name="subtype_no_language_dvorak" msgid="3122976737669823935">"无语言(Dvorak 键盘)"</string>
-    <string name="subtype_no_language_colemak" msgid="4205992994906097244">"无语言(Colemak 键盘)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="8840928374394180189">"无语言(PC 键盘)"</string>
+    <string name="subtype_no_language_qwerty" msgid="2956121451616633133">"无语言(QWERTY 键盘)"</string>
+    <string name="subtype_no_language_qwertz" msgid="1177848172397202890">"无语言(QWERTZ 键盘)"</string>
+    <string name="subtype_no_language_azerty" msgid="8721460968141187394">"无语言(AZERTY 键盘)"</string>
+    <string name="subtype_no_language_dvorak" msgid="3122976737669823935">"无语言(Dvorak 键盘)"</string>
+    <string name="subtype_no_language_colemak" msgid="4205992994906097244">"无语言(Colemak 键盘)"</string>
+    <string name="subtype_no_language_pcqwerty" msgid="8840928374394180189">"无语言(PC 键盘)"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"自定义输入样式"</string>
     <string name="add_style" msgid="6163126614514489951">"添加样式"</string>
     <string name="add" msgid="8299699805688017798">"添加"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 0758075..0bbd30b 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -39,10 +39,8 @@
     <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"使語言切換鍵包含其他輸入法"</string>
     <string name="show_language_switch_key" msgid="5915478828318774384">"語言切換鍵"</string>
     <string name="show_language_switch_key_summary" msgid="7343403647474265713">"有多種輸入語言可選用時顯示切換鍵"</string>
-    <!-- no translation found for sliding_key_input_preview (6604262359510068370) -->
-    <skip />
-    <!-- no translation found for sliding_key_input_preview_summary (6340524345729093886) -->
-    <skip />
+    <string name="sliding_key_input_preview" msgid="6604262359510068370">"顯示滑動指示器"</string>
+    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"從 Shift 鍵或符號鍵滑動時顯示視覺提示"</string>
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"關閉彈出式鍵盤的延遲時間"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"不延遲"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"預設"</string>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 8822e8d..bab612b 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -313,6 +313,10 @@
     <!-- TODO: remove translatable=false attribute once text is stable -->
     <string name="research_log_uploader_name" translatable="false">Research Uploader Service</string>
 
+    <!-- Name for the research replaying service to be displayed to users.  [CHAR LIMIT=50] -->
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_log_replayer_name" translatable="false">Research Replayer Service</string>
+
     <!-- Preference for input language selection -->
     <string name="select_language">Input languages</string>
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java
index 28655a9..ab810a5 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java
@@ -28,7 +28,6 @@
 import com.android.inputmethod.keyboard.PointerTracker;
 import com.android.inputmethod.latin.CoordinateUtils;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResizableIntArray;
 import com.android.inputmethod.latin.SuggestedWords;
 
 /**
@@ -44,16 +43,17 @@
  * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewRoundRadius
  */
 public class GestureFloatingPreviewText extends AbstractDrawingPreview {
-    private static final class GesturePreviewTextParams {
-        public final int mGesturePreviewTextSize;
-        public final int mGesturePreviewTextColor;
+    protected static final class GesturePreviewTextParams {
         public final int mGesturePreviewTextOffset;
         public final int mGesturePreviewTextHeight;
-        public final int mGesturePreviewColor;
         public final float mGesturePreviewHorizontalPadding;
         public final float mGesturePreviewVerticalPadding;
         public final float mGesturePreviewRoundRadius;
-        public final Paint mTextPaint;
+
+        private final int mGesturePreviewTextSize;
+        private final int mGesturePreviewTextColor;
+        private final int mGesturePreviewColor;
+        private final Paint mPaint = new Paint();
 
         private static final char[] TEXT_HEIGHT_REFERENCE_CHAR = { 'M' };
 
@@ -73,37 +73,36 @@
             mGesturePreviewRoundRadius = mainKeyboardViewAttr.getDimension(
                     R.styleable.MainKeyboardView_gestureFloatingPreviewRoundRadius, 0.0f);
 
-            final Paint textPaint = new Paint();
-            textPaint.setAntiAlias(true);
-            textPaint.setTextAlign(Align.CENTER);
-            textPaint.setTextSize(mGesturePreviewTextSize);
-            mTextPaint = textPaint;
+            final Paint textPaint = getTextPaint();
             final Rect textRect = new Rect();
             textPaint.getTextBounds(TEXT_HEIGHT_REFERENCE_CHAR, 0, 1, textRect);
             mGesturePreviewTextHeight = textRect.height();
         }
+
+        public Paint getTextPaint() {
+            mPaint.setAntiAlias(true);
+            mPaint.setTextAlign(Align.CENTER);
+            mPaint.setTextSize(mGesturePreviewTextSize);
+            mPaint.setColor(mGesturePreviewTextColor);
+            return mPaint;
+        }
+
+        public Paint getBackgroundPaint() {
+            mPaint.setColor(mGesturePreviewColor);
+            return mPaint;
+        }
     }
 
-    protected final GesturePreviewTextParams mParams;
-    protected int mPreviewWordNum;
-    protected final RectF mGesturePreviewRectangle = new RectF();
-    protected int mHighlightedWordIndex;
-
-    private static final int PREVIEW_TEXT_ARRAY_CAPACITY = 10;
-    // These variables store the positions of preview words. In multi-preview mode, the gesture
-    // floating preview at most shows PREVIEW_TEXT_ARRAY_CAPACITY words.
-    protected final ResizableIntArray mPreviewTextXArray = new ResizableIntArray(
-            PREVIEW_TEXT_ARRAY_CAPACITY);
-    protected final ResizableIntArray mPreviewTextYArray = new ResizableIntArray(
-            PREVIEW_TEXT_ARRAY_CAPACITY);
-
-    protected SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
-    public final int[] mLastPointerCoords = CoordinateUtils.newInstance();
+    private final GesturePreviewTextParams mParams;
+    private final RectF mGesturePreviewRectangle = new RectF();
+    private int mPreviewTextX;
+    private int mPreviewTextY;
+    private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
+    private final int[] mLastPointerCoords = CoordinateUtils.newInstance();
 
     public GestureFloatingPreviewText(final View drawingView, final TypedArray typedArray) {
         super(drawingView);
         mParams = new GesturePreviewTextParams(typedArray);
-        mHighlightedWordIndex = 0;
     }
 
     public void setSuggetedWords(final SuggestedWords suggestedWords) {
@@ -114,13 +113,6 @@
         updatePreviewPosition();
     }
 
-    protected void drawText(final Canvas canvas, final String text, final float textX,
-            final float textY, final int color) {
-        final Paint paint = mParams.mTextPaint;
-        paint.setColor(color);
-        canvas.drawText(text, textX, textY, paint);
-    }
-
     @Override
     public void setPreviewPosition(final PointerTracker tracker) {
         final boolean needsToUpdateLastPointer =
@@ -142,14 +134,11 @@
                 || TextUtils.isEmpty(mSuggestedWords.getWord(0))) {
             return;
         }
-        final Paint paint = mParams.mTextPaint;
-        paint.setColor(mParams.mGesturePreviewColor);
         final float round = mParams.mGesturePreviewRoundRadius;
-        canvas.drawRoundRect(mGesturePreviewRectangle, round, round, paint);
+        canvas.drawRoundRect(
+                mGesturePreviewRectangle, round, round, mParams.getBackgroundPaint());
         final String text = mSuggestedWords.getWord(0);
-        final int textX = mPreviewTextXArray.get(0);
-        final int textY = mPreviewTextYArray.get(0);
-        drawText(canvas, text, textX, textY, mParams.mGesturePreviewTextColor);
+        canvas.drawText(text, mPreviewTextX, mPreviewTextY, mParams.getTextPaint());
     }
 
     /**
@@ -162,11 +151,10 @@
         }
         final String text = mSuggestedWords.getWord(0);
 
-        final Paint paint = mParams.mTextPaint;
         final RectF rectangle = mGesturePreviewRectangle;
 
         final int textHeight = mParams.mGesturePreviewTextHeight;
-        final float textWidth = paint.measureText(text);
+        final float textWidth = mParams.getTextPaint().measureText(text);
         final float hPad = mParams.mGesturePreviewHorizontalPadding;
         final float vPad = mParams.mGesturePreviewVerticalPadding;
         final float rectWidth = textWidth + hPad * 2.0f;
@@ -180,10 +168,8 @@
                 - mParams.mGesturePreviewTextOffset - rectHeight;
         rectangle.set(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
 
-        final int textX = (int)(rectX + hPad + textWidth / 2.0f);
-        final int textY = (int)(rectY + vPad) + textHeight;
-        mPreviewTextXArray.add(0, textX);
-        mPreviewTextYArray.add(0, textY);
+        mPreviewTextX = (int)(rectX + hPad + textWidth / 2.0f);
+        mPreviewTextY = (int)(rectY + vPad) + textHeight;
         // TODO: Should narrow the invalidate region.
         getDrawingView().invalidate();
     }
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 443ffa2..ef440c5 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -56,11 +56,11 @@
 
     private static final String DICTIONARY_PROJECTION[] = { "id" };
 
-    public static final String QUERY_PARAMETER_MAY_PROMPT_USER = "mayPrompt";
-    public static final String QUERY_PARAMETER_TRUE = "true";
-    public static final String QUERY_PARAMETER_DELETE_RESULT = "result";
-    public static final String QUERY_PARAMETER_SUCCESS = "success";
-    public static final String QUERY_PARAMETER_FAILURE = "failure";
+    private static final String QUERY_PARAMETER_MAY_PROMPT_USER = "mayPrompt";
+    private static final String QUERY_PARAMETER_TRUE = "true";
+    private static final String QUERY_PARAMETER_DELETE_RESULT = "result";
+    private static final String QUERY_PARAMETER_SUCCESS = "success";
+    private static final String QUERY_PARAMETER_FAILURE = "failure";
 
     // Prevents this class to be accidentally instantiated.
     private BinaryDictionaryFileDumper() {
diff --git a/java/src/com/android/inputmethod/research/LogStatement.java b/java/src/com/android/inputmethod/research/LogStatement.java
index 090c58e..1d83e1a 100644
--- a/java/src/com/android/inputmethod/research/LogStatement.java
+++ b/java/src/com/android/inputmethod/research/LogStatement.java
@@ -29,13 +29,12 @@
             "PointerTrackerCallListenerOnCodeInput";
     public static final String KEY_CODE = "code";
     public static final String VALUE_RESEARCH = "research";
-    public static final String TYPE_LATIN_KEYBOARD_VIEW_ON_LONG_PRESS =
-            "LatinKeyboardViewOnLongPress";
+    public static final String TYPE_MAIN_KEYBOARD_VIEW_ON_LONG_PRESS =
+            "MainKeyboardViewOnLongPress";
     public static final String ACTION = "action";
     public static final String VALUE_DOWN = "DOWN";
-    public static final String TYPE_LATIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENTS =
-            "LatinKeyboardViewProcessMotionEvents";
-    public static final String KEY_LOGGING_RELATED = "loggingRelated";
+    public static final String TYPE_MOTION_EVENT = "MotionEvent";
+    public static final String KEY_IS_LOGGING_RELATED = "isLoggingRelated";
 
     // Name specifying the LogStatement type.
     private final String mType;
diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java
index 608fab3..2e732fc 100644
--- a/java/src/com/android/inputmethod/research/LogUnit.java
+++ b/java/src/com/android/inputmethod/research/LogUnit.java
@@ -453,13 +453,12 @@
 
         // Look for the long press that started the invocation of the research key code input.
         final int indexOfLastLongPressBeforeResearchKey =
-                findLastIndexBefore(LogStatement.TYPE_LATIN_KEYBOARD_VIEW_ON_LONG_PRESS,
+                findLastIndexBefore(LogStatement.TYPE_MAIN_KEYBOARD_VIEW_ON_LONG_PRESS,
                         indexOfLastResearchKey);
 
         // Look for DOWN event preceding the long press
         final int indexOfLastDownEventBeforeLongPress =
-                findLastIndexContainingKeyValueBefore(
-                        LogStatement.TYPE_LATIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENTS,
+                findLastIndexContainingKeyValueBefore(LogStatement.TYPE_MOTION_EVENT,
                         LogStatement.ACTION, LogStatement.VALUE_DOWN,
                         indexOfLastLongPressBeforeResearchKey);
 
@@ -471,8 +470,8 @@
             final LogStatement logStatement = mLogStatementList.get(index);
             final String type = logStatement.getType();
             final Object[] values = mValuesList.get(index);
-            if (type.equals(LogStatement.TYPE_LATIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENTS)) {
-                logStatement.setValue(LogStatement.KEY_LOGGING_RELATED, values, true);
+            if (type.equals(LogStatement.TYPE_MOTION_EVENT)) {
+                logStatement.setValue(LogStatement.KEY_IS_LOGGING_RELATED, values, true);
             }
         }
         return true;
diff --git a/java/src/com/android/inputmethod/research/MotionEventReader.java b/java/src/com/android/inputmethod/research/MotionEventReader.java
index 36e75be..e59adfa 100644
--- a/java/src/com/android/inputmethod/research/MotionEventReader.java
+++ b/java/src/com/android/inputmethod/research/MotionEventReader.java
@@ -19,6 +19,8 @@
 import android.util.JsonReader;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
 
 import com.android.inputmethod.latin.define.ProductionFlag;
 
@@ -33,6 +35,14 @@
 public class MotionEventReader {
     private static final String TAG = MotionEventReader.class.getSimpleName();
     private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG;
+    // Assumes that MotionEvent.ACTION_MASK does not have all bits set.`
+    private static final int UNINITIALIZED_ACTION = ~MotionEvent.ACTION_MASK;
+    // No legitimate int is negative
+    private static final int UNINITIALIZED_INT = -1;
+    // No legitimate long is negative
+    private static final long UNINITIALIZED_LONG = -1L;
+    // No legitimate float is negative
+    private static final float UNINITIALIZED_FLOAT = -1.0f;
 
     public ReplayData readMotionEventData(final File file) {
         final ReplayData replayData = new ReplayData();
@@ -55,19 +65,82 @@
 
     static class ReplayData {
         final ArrayList<Integer> mActions = new ArrayList<Integer>();
-        final ArrayList<Integer> mXCoords = new ArrayList<Integer>();
-        final ArrayList<Integer> mYCoords = new ArrayList<Integer>();
+        final ArrayList<PointerProperties[]> mPointerPropertiesArrays
+                = new ArrayList<PointerProperties[]>();
+        final ArrayList<PointerCoords[]> mPointerCoordsArrays = new ArrayList<PointerCoords[]>();
         final ArrayList<Long> mTimes = new ArrayList<Long>();
     }
 
-    private void readLogStatement(final JsonReader jsonReader, final ReplayData replayData)
-            throws IOException {
+    /**
+     * Read motion data from a logStatement and store it in {@code replayData}.
+     *
+     * Two kinds of logStatements can be read.  In the first variant, the MotionEvent data is
+     * represented as attributes at the top level like so:
+     *
+     * <pre>
+     * {
+     *   "_ct": 1359590400000,
+     *   "_ut": 4381933,
+     *   "_ty": "MotionEvent",
+     *   "action": "UP",
+     *   "isLoggingRelated": false,
+     *   "x": 100,
+     *   "y": 200
+     * }
+     * </pre>
+     *
+     * In the second variant, there is a separate attribute for the MotionEvent that includes
+     * historical data if present:
+     *
+     * <pre>
+     * {
+     *   "_ct": 135959040000,
+     *   "_ut": 4382702,
+     *   "_ty": "MotionEvent",
+     *   "action": "MOVE",
+     *   "isLoggingRelated": false,
+     *   "motionEvent": {
+     *     "pointerIds": [
+     *       0
+     *     ],
+     *     "xyt": [
+     *       {
+     *         "t": 4382551,
+     *         "d": [
+     *           {
+     *             "x": 141.25,
+     *             "y": 151.8485107421875,
+     *             "toma": 101.82337188720703,
+     *             "tomi": 101.82337188720703,
+     *             "o": 0.0
+     *           }
+     *         ]
+     *       },
+     *       {
+     *         "t": 4382559,
+     *         "d": [
+     *           {
+     *             "x": 140.7266082763672,
+     *             "y": 151.8485107421875,
+     *             "toma": 101.82337188720703,
+     *             "tomi": 101.82337188720703,
+     *             "o": 0.0
+     *           }
+     *         ]
+     *       }
+     *     ]
+     *   }
+     * },
+     * </pre>
+     */
+    /* package for test */ void readLogStatement(final JsonReader jsonReader,
+            final ReplayData replayData) throws IOException {
         String logStatementType = null;
-        Integer actionType = null;
-        Integer x = null;
-        Integer y = null;
-        Long time = null;
-        boolean loggingRelated = false;
+        int actionType = UNINITIALIZED_ACTION;
+        int x = UNINITIALIZED_INT;
+        int y = UNINITIALIZED_INT;
+        long time = UNINITIALIZED_LONG;
+        boolean isLoggingRelated = false;
 
         jsonReader.beginObject();
         while (jsonReader.hasNext()) {
@@ -90,7 +163,18 @@
                     actionType = MotionEvent.ACTION_MOVE;
                 }
             } else if (key.equals("loggingRelated")) {
-                loggingRelated = jsonReader.nextBoolean();
+                isLoggingRelated = jsonReader.nextBoolean();
+            } else if (logStatementType != null && logStatementType.equals("MotionEvent")
+                    && key.equals("motionEvent")) {
+                if (actionType == UNINITIALIZED_ACTION) {
+                    Log.e(TAG, "no actionType assigned in MotionEvent json");
+                }
+                // Second variant of LogStatement.
+                if (isLoggingRelated) {
+                    jsonReader.skipValue();
+                } else {
+                    readEmbeddedMotionEvent(jsonReader, replayData, actionType);
+                }
             } else {
                 if (DEBUG) {
                     Log.w(TAG, "Unknown JSON key in LogStatement: " + key);
@@ -100,14 +184,149 @@
         }
         jsonReader.endObject();
 
-        if (logStatementType != null && time != null && x != null && y != null && actionType != null
-                && logStatementType.equals("MainKeyboardViewProcessMotionEvent")
-                && !loggingRelated) {
-            replayData.mActions.add(actionType);
-            replayData.mXCoords.add(x);
-            replayData.mYCoords.add(y);
-            replayData.mTimes.add(time);
+        if (logStatementType != null && time != UNINITIALIZED_LONG && x != UNINITIALIZED_INT
+                && y != UNINITIALIZED_INT && actionType != UNINITIALIZED_ACTION
+                && logStatementType.equals("MotionEvent") && !isLoggingRelated) {
+            // First variant of LogStatement.
+            final PointerProperties pointerProperties = new PointerProperties();
+            pointerProperties.id = 0;
+            pointerProperties.toolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+            final PointerProperties[] pointerPropertiesArray = {
+                pointerProperties
+            };
+            final PointerCoords pointerCoords = new PointerCoords();
+            pointerCoords.x = x;
+            pointerCoords.y = y;
+            pointerCoords.pressure = 1.0f;
+            pointerCoords.size = 1.0f;
+            final PointerCoords[] pointerCoordsArray = {
+                pointerCoords
+            };
+            addMotionEventData(replayData, actionType, time, pointerPropertiesArray,
+                    pointerCoordsArray);
         }
     }
 
+    private void readEmbeddedMotionEvent(final JsonReader jsonReader, final ReplayData replayData,
+            final int actionType) throws IOException {
+        jsonReader.beginObject();
+        PointerProperties[] pointerPropertiesArray = null;
+        while (jsonReader.hasNext()) {  // pointerIds/xyt
+            final String name = jsonReader.nextName();
+            if (name.equals("pointerIds")) {
+                pointerPropertiesArray = readPointerProperties(jsonReader);
+            } else if (name.equals("xyt")) {
+                readPointerData(jsonReader, replayData, actionType, pointerPropertiesArray);
+            }
+        }
+        jsonReader.endObject();
+    }
+
+    private PointerProperties[] readPointerProperties(final JsonReader jsonReader)
+            throws IOException {
+        final ArrayList<PointerProperties> pointerPropertiesArrayList =
+                new ArrayList<PointerProperties>();
+        jsonReader.beginArray();
+        while (jsonReader.hasNext()) {
+            final PointerProperties pointerProperties = new PointerProperties();
+            pointerProperties.id = jsonReader.nextInt();
+            pointerProperties.toolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+            pointerPropertiesArrayList.add(pointerProperties);
+        }
+        jsonReader.endArray();
+        return pointerPropertiesArrayList.toArray(
+                new PointerProperties[pointerPropertiesArrayList.size()]);
+    }
+
+    private void readPointerData(final JsonReader jsonReader, final ReplayData replayData,
+            final int actionType, final PointerProperties[] pointerPropertiesArray)
+            throws IOException {
+        if (pointerPropertiesArray == null) {
+            Log.e(TAG, "PointerIDs must be given before xyt data in json for MotionEvent");
+            jsonReader.skipValue();
+            return;
+        }
+        long time = UNINITIALIZED_LONG;
+        jsonReader.beginArray();
+        while (jsonReader.hasNext()) {  // Array of historical data
+            jsonReader.beginObject();
+            final ArrayList<PointerCoords> pointerCoordsArrayList = new ArrayList<PointerCoords>();
+            while (jsonReader.hasNext()) {  // Time/data object
+                final String name = jsonReader.nextName();
+                if (name.equals("t")) {
+                    time = jsonReader.nextLong();
+                } else if (name.equals("d")) {
+                    jsonReader.beginArray();
+                    while (jsonReader.hasNext()) {  // array of data per pointer
+                        final PointerCoords pointerCoords = readPointerCoords(jsonReader);
+                        if (pointerCoords != null) {
+                            pointerCoordsArrayList.add(pointerCoords);
+                        }
+                    }
+                    jsonReader.endArray();
+                } else {
+                    jsonReader.skipValue();
+                }
+            }
+            jsonReader.endObject();
+            // Data was recorded as historical events, but must be split apart into
+            // separate MotionEvents for replaying
+            if (time != UNINITIALIZED_LONG) {
+                addMotionEventData(replayData, actionType, time, pointerPropertiesArray,
+                        pointerCoordsArrayList.toArray(
+                                new PointerCoords[pointerCoordsArrayList.size()]));
+            } else {
+                Log.e(TAG, "Time not assigned in json for MotionEvent");
+            }
+        }
+        jsonReader.endArray();
+    }
+
+    private PointerCoords readPointerCoords(final JsonReader jsonReader) throws IOException {
+        jsonReader.beginObject();
+        float x = UNINITIALIZED_FLOAT;
+        float y = UNINITIALIZED_FLOAT;
+        while (jsonReader.hasNext()) {  // x,y
+            final String name = jsonReader.nextName();
+            if (name.equals("x")) {
+                x = (float) jsonReader.nextDouble();
+            } else if (name.equals("y")) {
+                y = (float) jsonReader.nextDouble();
+            } else {
+                jsonReader.skipValue();
+            }
+        }
+        jsonReader.endObject();
+
+        if (Float.compare(x, UNINITIALIZED_FLOAT) == 0
+                || Float.compare(y, UNINITIALIZED_FLOAT) == 0) {
+            Log.w(TAG, "missing x or y value in MotionEvent json");
+            return null;
+        }
+        final PointerCoords pointerCoords = new PointerCoords();
+        pointerCoords.x = x;
+        pointerCoords.y = y;
+        pointerCoords.pressure = 1.0f;
+        pointerCoords.size = 1.0f;
+        return pointerCoords;
+    }
+
+    /**
+     * Tests that {@code x} is uninitialized.
+     *
+     * Assumes that {@code x} will never be given a valid value less than 0, and that
+     * UNINITIALIZED_FLOAT is less than 0.0f.
+     */
+    private boolean isUninitializedFloat(final float x) {
+        return x < 0.0f;
+    }
+
+    private void addMotionEventData(final ReplayData replayData, final int actionType,
+            final long time, final PointerProperties[] pointerProperties,
+            final PointerCoords[] pointerCoords) {
+        replayData.mActions.add(actionType);
+        replayData.mTimes.add(time);
+        replayData.mPointerPropertiesArrays.add(pointerProperties);
+        replayData.mPointerCoordsArrays.add(pointerCoords);
+    }
 }
diff --git a/java/src/com/android/inputmethod/research/Replayer.java b/java/src/com/android/inputmethod/research/Replayer.java
index 4cc2a58..a9b7a9d 100644
--- a/java/src/com/android/inputmethod/research/Replayer.java
+++ b/java/src/com/android/inputmethod/research/Replayer.java
@@ -1,26 +1,29 @@
 /*
  * Copyright (C) 2012 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
  *
- * http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package com.android.inputmethod.research;
 
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
+import android.view.MotionEvent.PointerProperties;
 
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.MainKeyboardView;
@@ -40,6 +43,14 @@
     private boolean mIsReplaying = false;
     private KeyboardSwitcher mKeyboardSwitcher;
 
+    private Replayer() {
+    }
+
+    private static final Replayer sInstance = new Replayer();
+    public static Replayer getInstance() {
+        return sInstance;
+    }
+
     public void setKeyboardSwitcher(final KeyboardSwitcher keyboardSwitcher) {
         mKeyboardSwitcher = keyboardSwitcher;
     }
@@ -49,11 +60,10 @@
     private static final int COMPLETION_TIME_MS = 500;
 
     // TODO: Support historical events and multi-touch.
-    public void replay(final ReplayData replayData) {
+    public void replay(final ReplayData replayData, final Runnable callback) {
         if (mIsReplaying) {
             return;
         }
-
         mIsReplaying = true;
         final int numActions = replayData.mActions.size();
         if (DEBUG) {
@@ -72,7 +82,7 @@
         // The adjustment needed to translate times from the original recorded time to the current
         // time.
         final long timeAdjustment = currentStartTime - origStartTime;
-        final Handler handler = new Handler() {
+        final Handler handler = new Handler(Looper.getMainLooper()) {
             // Track the time of the most recent DOWN event, to be passed as a parameter when
             // constructing a MotionEvent.  It's initialized here to the origStartTime, but this is
             // only a precaution.  The value should be overwritten by the first ACTION_DOWN event
@@ -86,25 +96,36 @@
                 case MSG_MOTION_EVENT:
                     final int index = msg.arg1;
                     final int action = replayData.mActions.get(index);
-                    final int x = replayData.mXCoords.get(index);
-                    final int y = replayData.mYCoords.get(index);
+                    final PointerProperties[] pointerPropertiesArray =
+                            replayData.mPointerPropertiesArrays.get(index);
+                    final PointerCoords[] pointerCoordsArray =
+                            replayData.mPointerCoordsArrays.get(index);
                     final long origTime = replayData.mTimes.get(index);
                     if (action == MotionEvent.ACTION_DOWN) {
                         mOrigDownTime = origTime;
                     }
 
                     final MotionEvent me = MotionEvent.obtain(mOrigDownTime + timeAdjustment,
-                            origTime + timeAdjustment, action, x, y, 0);
+                            origTime + timeAdjustment, action,
+                            pointerPropertiesArray.length, pointerPropertiesArray,
+                            pointerCoordsArray, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0);
                     mainKeyboardView.processMotionEvent(me);
                     me.recycle();
                     break;
                 case MSG_DONE:
                     mIsReplaying = false;
+                    ResearchLogger.getInstance().requestIndicatorRedraw();
                     break;
                 }
             }
         };
 
+        handler.post(new Runnable() {
+            @Override
+            public void run() {
+                ResearchLogger.getInstance().requestIndicatorRedraw();
+            }
+        });
         for (int i = 0; i < numActions; i++) {
             final Message msg = Message.obtain(handler, MSG_MOTION_EVENT, i, 0);
             final long msgTime = replayData.mTimes.get(i) + timeAdjustment;
@@ -113,8 +134,16 @@
                 Log.d(TAG, "queuing event at " + msgTime);
             }
         }
+
         final long presentDoneTime = replayData.mTimes.get(numActions - 1) + timeAdjustment
                 + COMPLETION_TIME_MS;
         handler.sendMessageAtTime(Message.obtain(handler, MSG_DONE), presentDoneTime);
+        if (callback != null) {
+            handler.postAtTime(callback, presentDoneTime + 1);
+        }
+    }
+
+    public boolean isReplaying() {
+        return mIsReplaying;
     }
 }
diff --git a/java/src/com/android/inputmethod/research/ReplayerService.java b/java/src/com/android/inputmethod/research/ReplayerService.java
new file mode 100644
index 0000000..88d9033
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/ReplayerService.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.inputmethod.research.MotionEventReader.ReplayData;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Provide a mechanism to invoke the replayer from outside.
+ *
+ * In particular, makes access from a host possible through {@code adb am startservice}.
+ */
+public class ReplayerService extends IntentService {
+    private static final String TAG = ReplayerService.class.getSimpleName();
+    private static final String EXTRA_FILENAME = "com.android.inputmethod.research.extra.FILENAME";
+    private static final long MAX_REPLAY_TIME = TimeUnit.SECONDS.toMillis(60);
+
+    public ReplayerService() {
+        super(ReplayerService.class.getSimpleName());
+    }
+
+    @Override
+    protected void onHandleIntent(final Intent intent) {
+        final String filename = intent.getStringExtra(EXTRA_FILENAME);
+        if (filename == null) return;
+
+        final ReplayData replayData = new MotionEventReader().readMotionEventData(
+                new File(filename));
+        synchronized (this) {
+            Replayer.getInstance().replay(replayData, new Runnable() {
+                @Override
+                public void run() {
+                    synchronized (ReplayerService.this) {
+                        ReplayerService.this.notify();
+                    }
+                }
+            });
+            try {
+                wait(MAX_REPLAY_TIME);
+            } catch (InterruptedException e) {
+                Log.e(TAG, "Timeout while replaying.", e);
+            }
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 535ce28..8fc62ea 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -191,10 +191,10 @@
     private Suggest mSuggest;
     private MainKeyboardView mMainKeyboardView;
     // TODO: Check whether a superclass can be used instead of LatinIME.
-    private LatinIME mLatinIME;
+    /* package for test */ LatinIME mLatinIME;
     private final Statistics mStatistics;
     private final MotionEventReader mMotionEventReader = new MotionEventReader();
-    private final Replayer mReplayer = new Replayer();
+    private final Replayer mReplayer = Replayer.getInstance();
 
     private Intent mUploadIntent;
     private Intent mUploadNowIntent;
@@ -798,7 +798,7 @@
                 public void run() {
                     final ReplayData replayData =
                             mMotionEventReader.readMotionEventData(mUserRecordingFile);
-                    mReplayer.replay(replayData);
+                    mReplayer.replay(replayData, null);
                 }
             }, 1000);
         }
@@ -846,7 +846,8 @@
     }
 
     private boolean isAllowedToLog() {
-        return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog;
+        return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog
+                && !isReplaying();
     }
 
     public void requestIndicatorRedraw() {
@@ -859,15 +860,30 @@
         mMainKeyboardView.invalidateAllKeys();
     }
 
+    private boolean isReplaying() {
+        return mReplayer.isReplaying();
+    }
+
+    private int getIndicatorColor() {
+        if (isMakingUserRecording()) {
+            return Color.YELLOW;
+        }
+        if (isReplaying()) {
+            return Color.GREEN;
+        }
+        return Color.RED;
+    }
+
     public void paintIndicator(KeyboardView view, Paint paint, Canvas canvas, int width,
             int height) {
         // TODO: Reimplement using a keyboard background image specific to the ResearchLogger
         // and remove this method.
         // The check for MainKeyboardView ensures that the indicator only decorates the main
         // keyboard, not every keyboard.
-        if (IS_SHOWING_INDICATOR && isAllowedToLog() && view instanceof MainKeyboardView) {
+        if (IS_SHOWING_INDICATOR && (isAllowedToLog() || isReplaying())
+                && view instanceof MainKeyboardView) {
             final int savedColor = paint.getColor();
-            paint.setColor(isMakingUserRecording() ? Color.YELLOW : Color.RED);
+            paint.setColor(getIndicatorColor());
             final Style savedStyle = paint.getStyle();
             paint.setStyle(Style.STROKE);
             final float savedStrokeWidth = paint.getStrokeWidth();
@@ -1189,7 +1205,8 @@
      *
      */
     private static final LogStatement LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT =
-            new LogStatement("MotionEvent", true, false, "action", "MotionEvent", "loggingRelated");
+            new LogStatement("MotionEvent", true, false, "action",
+                    LogStatement.KEY_IS_LOGGING_RELATED, "motionEvent");
     public static void mainKeyboardView_processMotionEvent(final MotionEvent me, final int action,
             final long eventTime, final int index, final int id, final int x, final int y) {
         if (me != null) {
@@ -1206,7 +1223,7 @@
             }
             final ResearchLogger researchLogger = getInstance();
             researchLogger.enqueueEvent(LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT,
-                    actionString, MotionEvent.obtain(me), false);
+                    actionString, false /* IS_LOGGING_RELATED */, MotionEvent.obtain(me));
             if (action == MotionEvent.ACTION_DOWN) {
                 // Subtract 1 from eventTime so the down event is included in the later
                 // LogUnit, not the earlier (the test is for inequality).
diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h
index 83676b2..ecdddd7 100644
--- a/native/jni/src/dictionary.h
+++ b/native/jni/src/dictionary.h
@@ -31,15 +31,15 @@
 class Dictionary {
  public:
     // Taken from SuggestedWords.java
-    const static int KIND_TYPED = 0; // What user typed
-    const static int KIND_CORRECTION = 1; // Simple correction/suggestion
-    const static int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
-    const static int KIND_WHITELIST = 3; // Whitelisted word
-    const static int KIND_BLACKLIST = 4; // Blacklisted word
-    const static int KIND_HARDCODED = 5; // Hardcoded suggestion, e.g. punctuation
-    const static int KIND_APP_DEFINED = 6; // Suggested by the application
-    const static int KIND_SHORTCUT = 7; // A shortcut
-    const static int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
+    static const int KIND_TYPED = 0; // What user typed
+    static const int KIND_CORRECTION = 1; // Simple correction/suggestion
+    static const int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
+    static const int KIND_WHITELIST = 3; // Whitelisted word
+    static const int KIND_BLACKLIST = 4; // Blacklisted word
+    static const int KIND_HARDCODED = 5; // Hardcoded suggestion, e.g. punctuation
+    static const int KIND_APP_DEFINED = 6; // Suggested by the application
+    static const int KIND_SHORTCUT = 7; // A shortcut
+    static const int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
 
     Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust);
 
diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp
index a0bad1a..d812745 100644
--- a/native/jni/src/proximity_info.cpp
+++ b/native/jni/src/proximity_info.cpp
@@ -24,11 +24,10 @@
 #include "geometry_utils.h"
 #include "jni.h"
 #include "proximity_info.h"
+#include "proximity_info_params.h"
 
 namespace latinime {
 
-/* static */ const float ProximityInfo::NOT_A_DISTANCE_FLOAT = -1.0f;
-
 static AK_FORCE_INLINE void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray,
         jsize len, jint *buffer) {
     if (jArray && buffer) {
@@ -129,17 +128,15 @@
 
 float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG(
         const int keyId, const int x, const int y) const {
-    const static float verticalSweetSpotScaleForGeometric = 1.1f;
     const bool correctTouchPosition = hasTouchPositionCorrectionData();
-    const float centerX = static_cast<float>(correctTouchPosition
-            ? getSweetSpotCenterXAt(keyId)
+    const float centerX = static_cast<float>(correctTouchPosition ? getSweetSpotCenterXAt(keyId)
             : getKeyCenterXOfKeyIdG(keyId));
     const float visualKeyCenterY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId));
     float centerY;
     if (correctTouchPosition) {
         const float sweetSpotCenterY = static_cast<float>(getSweetSpotCenterYAt(keyId));
         const float gapY = sweetSpotCenterY - visualKeyCenterY;
-        centerY = visualKeyCenterY + gapY * verticalSweetSpotScaleForGeometric;
+        centerY = visualKeyCenterY + gapY * ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE_G;
     } else {
         centerY = visualKeyCenterY;
     }
diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h
index f3a68e4..d42c723 100644
--- a/native/jni/src/proximity_info.h
+++ b/native/jni/src/proximity_info.h
@@ -83,7 +83,6 @@
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfo);
-    static const float NOT_A_DISTANCE_FLOAT;
 
     void initializeG();
     float calculateNormalizedSquaredDistance(const int keyIndex, const int inputIndex) const;
diff --git a/native/jni/src/proximity_info_params.cpp b/native/jni/src/proximity_info_params.cpp
index f7b3d4d..1410ab5 100644
--- a/native/jni/src/proximity_info_params.cpp
+++ b/native/jni/src/proximity_info_params.cpp
@@ -14,22 +14,25 @@
  * limitations under the License.
  */
 
+#include "defines.h"
 #include "proximity_info_params.h"
 
 namespace latinime {
-const int ProximityInfoParams::LOOKUP_RADIUS_PERCENTILE = 50;
-const int ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS = 150;
-const int ProximityInfoParams::STRONG_DOUBLE_LETTER_TIME_MILLIS = 600;
-const int ProximityInfoParams::MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE = 5;
-const int ProximityInfoParams::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10;
-const int ProximityInfoParams::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
-        1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
 const float ProximityInfoParams::NOT_A_DISTANCE_FLOAT = -1.0f;
+const int ProximityInfoParams::MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE = 5;
+const float ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE_G = 1.1f;
 
-// Per method constants
+/* Per method constants */
+// Used by ProximityInfoStateUtils::initGeometricDistanceInfos()
 const float ProximityInfoParams::NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD = 4.0f;
+
+// Used by ProximityInfoStateUtils::updateNearKeysDistances()
 const float ProximityInfoParams::NEAR_KEY_THRESHOLD_FOR_DISTANCE = 2.0f;
+
+// Used by ProximityInfoStateUtils::isPrevLocalMin()
 const float ProximityInfoParams::MARGIN_FOR_PREV_LOCAL_MIN = 0.01f;
+
+// Used by ProximityInfoStateUtils::getPointScore()
 const int ProximityInfoParams::DISTANCE_BASE_SCALE = 100;
 const float ProximityInfoParams::NEAR_KEY_THRESHOLD_FOR_POINT_SCORE = 0.6f;
 const int ProximityInfoParams::CORNER_CHECK_DISTANCE_THRESHOLD_SCALE = 25;
@@ -39,6 +42,62 @@
 const float ProximityInfoParams::CORNER_SUM_ANGLE_THRESHOLD = M_PI_F / 4.0f;
 const float ProximityInfoParams::CORNER_SCORE = 1.0f;
 
+// Used by ProximityInfoStateUtils::refreshSpeedRates()
+const int ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION = 2;
+
+// Used by ProximityInfoStateUtils::pushTouchPoint()
+const int ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE = 4;
+
+// Used by ProximityInfoStateUtils::updateAlignPointProbabilities()
+const float ProximityInfoParams::MIN_PROBABILITY = 0.000001f;
+const float ProximityInfoParams::MAX_SKIP_PROBABILITY = 0.95f;
+const float ProximityInfoParams::SKIP_FIRST_POINT_PROBABILITY = 0.01f;
+const float ProximityInfoParams::SKIP_LAST_POINT_PROBABILITY = 0.1f;
+const float ProximityInfoParams::MIN_SPEED_RATE_FOR_SKIP_PROBABILITY = 0.15f;
+const float ProximityInfoParams::SPEED_WEIGHT_FOR_SKIP_PROBABILITY = 0.9f;
+const float ProximityInfoParams::SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY = 0.6f;
+const float ProximityInfoParams::NEAREST_DISTANCE_WEIGHT = 0.5f;
+const float ProximityInfoParams::NEAREST_DISTANCE_BIAS = 0.5f;
+const float ProximityInfoParams::NEAREST_DISTANCE_WEIGHT_FOR_LAST = 0.6f;
+const float ProximityInfoParams::NEAREST_DISTANCE_BIAS_FOR_LAST = 0.4f;
+const float ProximityInfoParams::ANGLE_WEIGHT = 0.90f;
+const float ProximityInfoParams::DEEP_CORNER_ANGLE_THRESHOLD = M_PI_F * 60.0f / 180.0f;
+const float ProximityInfoParams::SKIP_DEEP_CORNER_PROBABILITY = 0.1f;
+const float ProximityInfoParams::CORNER_ANGLE_THRESHOLD = M_PI_F * 30.0f / 180.0f;
+const float ProximityInfoParams::STRAIGHT_ANGLE_THRESHOLD = M_PI_F * 15.0f / 180.0f;
+const float ProximityInfoParams::SKIP_CORNER_PROBABILITY = 0.4f;
+const float ProximityInfoParams::SPEED_MARGIN = 0.1f;
+const float ProximityInfoParams::CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION = 0.0f;
+// TODO: The variance is critical for accuracy; thus, adjusting these parameter by machine
+// learning or something would be efficient.
+const float ProximityInfoParams::SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION = 0.3f;
+const float ProximityInfoParams::MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION = 0.25f;
+const float ProximityInfoParams::SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION = 0.5f;
+const float ProximityInfoParams::MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION = 0.15f;
+const float ProximityInfoParams::MIN_STANDERD_DIVIATION = 0.37f;
+const float ProximityInfoParams::PREV_DISTANCE_WEIGHT = 0.5f;
+const float ProximityInfoParams::NEXT_DISTANCE_WEIGHT = 0.6f;
+
+// Used by ProximityInfoStateUtils::suppressCharProbabilities()
+const float ProximityInfoParams::SUPPRESSION_LENGTH_WEIGHT = 1.5f;
+const float ProximityInfoParams::MIN_SUPPRESSION_RATE = 0.1f;
+const float ProximityInfoParams::SUPPRESSION_WEIGHT = 0.5f;
+const float ProximityInfoParams::SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN = 0.1f;
+const float ProximityInfoParams::SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN = 0.3f;
+
+// Used by ProximityInfoStateUtils::getMostProbableString()
+const float ProximityInfoParams::DEMOTION_LOG_PROBABILITY = 0.3f;
+
+// Used by ProximityInfoStateUtils::updateSampledSearchKeysVector()
 // TODO: Investigate if this is required
 const float ProximityInfoParams::SEARCH_KEY_RADIUS_RATIO = 0.95f;
+
+// Used by ProximityInfoStateUtils::calculateBeelineSpeedRate()
+const int ProximityInfoParams::LOOKUP_RADIUS_PERCENTILE = 50;
+const int ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS = 150;
+const int ProximityInfoParams::STRONG_DOUBLE_LETTER_TIME_MILLIS = 600;
+
+// Used by ProximityInfoStateUtils::calculateNormalizedSquaredDistance()
+const int ProximityInfoParams::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR = 1 << 10;
+
 } // namespace latinime
diff --git a/native/jni/src/proximity_info_params.h b/native/jni/src/proximity_info_params.h
index 978b999..7c26208 100644
--- a/native/jni/src/proximity_info_params.h
+++ b/native/jni/src/proximity_info_params.h
@@ -23,13 +23,9 @@
 
 class ProximityInfoParams {
  public:
-    static const int LOOKUP_RADIUS_PERCENTILE;
-    static const int FIRST_POINT_TIME_OFFSET_MILLIS;
-    static const int STRONG_DOUBLE_LETTER_TIME_MILLIS;
-    static const int MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE;
-    static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
     static const float NOT_A_DISTANCE_FLOAT;
-    static const float SEARCH_KEY_RADIUS_RATIO;
+    static const int MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE;
+    static const float VERTICAL_SWEET_SPOT_SCALE_G;
 
     // Used by ProximityInfoStateUtils::initGeometricDistanceInfos()
     static const float NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD;
@@ -50,9 +46,63 @@
     static const float CORNER_SUM_ANGLE_THRESHOLD;
     static const float CORNER_SCORE;
 
+    // Used by ProximityInfoStateUtils::refreshSpeedRates()
+    static const int NUM_POINTS_FOR_SPEED_CALCULATION;
+
+    // Used by ProximityInfoStateUtils::pushTouchPoint()
+    static const int LAST_POINT_SKIP_DISTANCE_SCALE;
+
+    // Used by ProximityInfoStateUtils::updateAlignPointProbabilities()
+    static const float MIN_PROBABILITY;
+    static const float MAX_SKIP_PROBABILITY;
+    static const float SKIP_FIRST_POINT_PROBABILITY;
+    static const float SKIP_LAST_POINT_PROBABILITY;
+    static const float MIN_SPEED_RATE_FOR_SKIP_PROBABILITY;
+    static const float SPEED_WEIGHT_FOR_SKIP_PROBABILITY;
+    static const float SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY;
+    static const float NEAREST_DISTANCE_WEIGHT;
+    static const float NEAREST_DISTANCE_BIAS;
+    static const float NEAREST_DISTANCE_WEIGHT_FOR_LAST;
+    static const float NEAREST_DISTANCE_BIAS_FOR_LAST;
+    static const float ANGLE_WEIGHT;
+    static const float DEEP_CORNER_ANGLE_THRESHOLD;
+    static const float SKIP_DEEP_CORNER_PROBABILITY;
+    static const float CORNER_ANGLE_THRESHOLD;
+    static const float STRAIGHT_ANGLE_THRESHOLD;
+    static const float SKIP_CORNER_PROBABILITY;
+    static const float SPEED_MARGIN;
+    static const float CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION;
+    static const float SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION;
+    static const float MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION;
+    static const float SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION;
+    static const float MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION;
+    static const float MIN_STANDERD_DIVIATION;
+    static const float PREV_DISTANCE_WEIGHT;
+    static const float NEXT_DISTANCE_WEIGHT;
+
+    // Used by ProximityInfoStateUtils::suppressCharProbabilities()
+    static const float SUPPRESSION_LENGTH_WEIGHT;
+    static const float MIN_SUPPRESSION_RATE;
+    static const float SUPPRESSION_WEIGHT;
+    static const float SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN;
+    static const float SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN;
+
+    // Used by ProximityInfoStateUtils::getMostProbableString()
+    static const float DEMOTION_LOG_PROBABILITY;
+
+    // Used by ProximityInfoStateUtils::updateSampledSearchKeysVector()
+    static const float SEARCH_KEY_RADIUS_RATIO;
+
+    // Used by ProximityInfoStateUtils::calculateBeelineSpeedRate()
+    static const int LOOKUP_RADIUS_PERCENTILE;
+    static const int FIRST_POINT_TIME_OFFSET_MILLIS;
+    static const int STRONG_DOUBLE_LETTER_TIME_MILLIS;
+
+    // Used by ProximityInfoStateUtils::calculateNormalizedSquaredDistance()
+    static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
+
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfoParams);
-    static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
 };
 } // namespace latinime
 #endif // LATINIME_PROXIMITY_INFO_PARAMS_H
diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp
index fbdc2c8..4c1ffb3 100644
--- a/native/jni/src/proximity_info_state.cpp
+++ b/native/jni/src/proximity_info_state.cpp
@@ -89,29 +89,28 @@
     }
 
     if (xCoordinates && yCoordinates) {
-        mSampledInputSize = ProximityInfoStateUtils::updateTouchPoints(
-                mProximityInfo->getMostCommonKeyWidth(), mProximityInfo, mMaxPointToKeyLength,
-                mInputProximities, xCoordinates, yCoordinates, times, pointerIds, inputSize,
-                isGeometric, pointerId, pushTouchPointStartIndex, &mSampledInputXs,
-                &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, &mSampledInputIndice);
+        mSampledInputSize = ProximityInfoStateUtils::updateTouchPoints(mProximityInfo,
+                mMaxPointToKeyLength, mInputProximities, xCoordinates, yCoordinates, times,
+                pointerIds, inputSize, isGeometric, pointerId, pushTouchPointStartIndex,
+                &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledLengthCache,
+                &mSampledInputIndice);
     }
 
     if (mSampledInputSize > 0 && isGeometric) {
-        mAverageSpeed = ProximityInfoStateUtils::refreshSpeedRates(
-                inputSize, xCoordinates, yCoordinates, times, lastSavedInputSize,
-                mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes,
-                &mSampledLengthCache, &mSampledInputIndice, &mSpeedRates, &mDirections);
-        ProximityInfoStateUtils::refreshBeelineSpeedRates(
-                mProximityInfo->getMostCommonKeyWidth(), mAverageSpeed, inputSize,
-                xCoordinates, yCoordinates, times, mSampledInputSize, &mSampledInputXs,
-                &mSampledInputYs, &mSampledInputIndice, &mBeelineSpeedPercentiles);
+        mAverageSpeed = ProximityInfoStateUtils::refreshSpeedRates(inputSize, xCoordinates,
+                yCoordinates, times, lastSavedInputSize, mSampledInputSize, &mSampledInputXs,
+                &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, &mSampledInputIndice,
+                &mSpeedRates, &mDirections);
+        ProximityInfoStateUtils::refreshBeelineSpeedRates(mProximityInfo->getMostCommonKeyWidth(),
+                mAverageSpeed, inputSize, xCoordinates, yCoordinates, times, mSampledInputSize,
+                &mSampledInputXs, &mSampledInputYs, &mSampledInputIndice,
+                &mBeelineSpeedPercentiles);
     }
 
     if (mSampledInputSize > 0) {
-        ProximityInfoStateUtils::initGeometricDistanceInfos(
-                mProximityInfo, mProximityInfo->getKeyCount(),
-                mSampledInputSize, lastSavedInputSize, &mSampledInputXs, &mSampledInputYs,
-                &mSampledNearKeysVector, &mSampledDistanceCache_G);
+        ProximityInfoStateUtils::initGeometricDistanceInfos(mProximityInfo, mSampledInputSize,
+                lastSavedInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledNearKeysVector,
+                &mSampledDistanceCache_G);
         if (isGeometric) {
             // updates probabilities of skipping or mapping each key for all points.
             ProximityInfoStateUtils::updateAlignPointProbabilities(
diff --git a/native/jni/src/proximity_info_state_utils.cpp b/native/jni/src/proximity_info_state_utils.cpp
index da3f03d..f9b69d2 100644
--- a/native/jni/src/proximity_info_state_utils.cpp
+++ b/native/jni/src/proximity_info_state_utils.cpp
@@ -38,7 +38,7 @@
     return nextStartIndex;
 }
 
-/* static */ int ProximityInfoStateUtils::updateTouchPoints(const int mostCommonKeyWidth,
+/* static */ int ProximityInfoStateUtils::updateTouchPoints(
         const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
         const int *const inputProximities, const int *const inputXCoordinates,
         const int *const inputYCoordinates, const int *const times, const int *const pointerIds,
@@ -106,15 +106,14 @@
                 const float prevAngle = getAngle(
                         inputXCoordinates[i - 2], inputYCoordinates[i - 2],
                         inputXCoordinates[i - 1], inputYCoordinates[i - 1]);
-                const float currentAngle =
-                        getAngle(inputXCoordinates[i - 1], inputYCoordinates[i - 1], x, y);
+                const float currentAngle = getAngle(
+                        inputXCoordinates[i - 1], inputYCoordinates[i - 1], x, y);
                 sumAngle += getAngleDiff(prevAngle, currentAngle);
             }
 
-            if (pushTouchPoint(mostCommonKeyWidth, proximityInfo, maxPointToKeyLength,
-                    i, c, x, y, time, isGeometric /* doSampling */,
-                    i == lastInputIndex, sumAngle, currentNearKeysDistances,
-                    prevNearKeysDistances, prevPrevNearKeysDistances,
+            if (pushTouchPoint(proximityInfo, maxPointToKeyLength, i, c, x, y, time,
+                    isGeometric /* doSampling */, i == lastInputIndex, sumAngle,
+                    currentNearKeysDistances, prevNearKeysDistances, prevPrevNearKeysDistances,
                     sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache,
                     sampledInputIndice)) {
                 // Previous point information was popped.
@@ -222,12 +221,13 @@
 }
 
 /* static */ void ProximityInfoStateUtils::initGeometricDistanceInfos(
-        const ProximityInfo *const proximityInfo, const int keyCount, const int sampledInputSize,
+        const ProximityInfo *const proximityInfo, const int sampledInputSize,
         const int lastSavedInputSize, const std::vector<int> *const sampledInputXs,
         const std::vector<int> *const sampledInputYs,
         std::vector<NearKeycodesSet> *SampledNearKeysVector,
         std::vector<float> *SampledDistanceCache_G) {
     SampledNearKeysVector->resize(sampledInputSize);
+    const int keyCount = proximityInfo->getKeyCount();
     SampledDistanceCache_G->resize(sampledInputSize * keyCount);
     for (int i = lastSavedInputSize; i < sampledInputSize; ++i) {
         (*SampledNearKeysVector)[i].reset();
@@ -275,10 +275,11 @@
         int duration = 0;
 
         // Calculate velocity by using distances and durations of
-        // NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and backward.
-        static const int NUM_POINTS_FOR_SPEED_CALCULATION = 2;
-        for (int j = index; j < min(inputSize - 1, index + NUM_POINTS_FOR_SPEED_CALCULATION);
-                ++j) {
+        // ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and
+        // backward.
+        const int forwardNumPoints = min(inputSize - 1,
+                index + ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION);
+        for (int j = index; j < forwardNumPoints; ++j) {
             if (i < sampledInputSize - 1 && j >= (*sampledInputIndice)[i + 1]) {
                 break;
             }
@@ -286,7 +287,9 @@
                     xCoordinates[j + 1], yCoordinates[j + 1]);
             duration += times[j + 1] - times[j];
         }
-        for (int j = index - 1; j >= max(0, index - NUM_POINTS_FOR_SPEED_CALCULATION); --j) {
+        const int backwardNumPoints = max(0,
+                index - ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION);
+        for (int j = index - 1; j >= backwardNumPoints; --j) {
             if (i > 0 && j < (*sampledInputIndice)[i - 1]) {
                 break;
             }
@@ -434,9 +437,8 @@
 
 // Sampling touch point and pushing information to vectors.
 // Returning if previous point is popped or not.
-/* static */ bool ProximityInfoStateUtils::pushTouchPoint(const int mostCommonKeyWidth,
-        const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
-        const int inputIndex, const int nodeCodePoint, int x, int y,
+/* static */ bool ProximityInfoStateUtils::pushTouchPoint(const ProximityInfo *const proximityInfo,
+        const int maxPointToKeyLength, const int inputIndex, const int nodeCodePoint, int x, int y,
         const int time, const bool doSampling, const bool isLastPoint, const float sumAngle,
         NearKeysDistanceMap *const currentNearKeysDistances,
         const NearKeysDistanceMap *const prevNearKeysDistances,
@@ -444,7 +446,7 @@
         std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs,
         std::vector<int> *sampledInputTimes, std::vector<int> *sampledLengthCache,
         std::vector<int> *sampledInputIndice) {
-    static const int LAST_POINT_SKIP_DISTANCE_SCALE = 4;
+    const int mostCommonKeyWidth = proximityInfo->getMostCommonKeyWidth();
 
     size_t size = sampledInputXs->size();
     bool popped = false;
@@ -465,16 +467,16 @@
         }
         // Check if the last point should be skipped.
         if (isLastPoint && size > 0) {
-            if (getDistanceInt(x, y, sampledInputXs->back(),
-                    sampledInputYs->back()) * LAST_POINT_SKIP_DISTANCE_SCALE
-                            < mostCommonKeyWidth) {
+            if (getDistanceInt(x, y, sampledInputXs->back(), sampledInputYs->back())
+                    * ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE < mostCommonKeyWidth) {
                 // This point is not used because it's too close to the previous point.
                 if (DEBUG_GEO_FULL) {
                     AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %d, "
                            "width = %d", size, x, y, sampledInputXs->back(),
                            sampledInputYs->back(), getDistanceInt(
                                    x, y, sampledInputXs->back(), sampledInputYs->back()),
-                           mostCommonKeyWidth / LAST_POINT_SKIP_DISTANCE_SCALE);
+                           mostCommonKeyWidth
+                                   / ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE);
                 }
                 return popped;
             }
@@ -664,35 +666,14 @@
         const std::vector<float> *const SampledDistanceCache_G,
         std::vector<NearKeycodesSet> *SampledNearKeysVector,
         std::vector<hash_map_compat<int, float> > *charProbabilities) {
-    static const float MIN_PROBABILITY = 0.000001f;
-    static const float MAX_SKIP_PROBABILITY = 0.95f;
-    static const float SKIP_FIRST_POINT_PROBABILITY = 0.01f;
-    static const float SKIP_LAST_POINT_PROBABILITY = 0.1f;
-    static const float MIN_SPEED_RATE_FOR_SKIP_PROBABILITY = 0.15f;
-    static const float SPEED_WEIGHT_FOR_SKIP_PROBABILITY = 0.9f;
-    static const float SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY = 0.6f;
-    static const float NEAREST_DISTANCE_WEIGHT = 0.5f;
-    static const float NEAREST_DISTANCE_BIAS = 0.5f;
-    static const float NEAREST_DISTANCE_WEIGHT_FOR_LAST = 0.6f;
-    static const float NEAREST_DISTANCE_BIAS_FOR_LAST = 0.4f;
-
-    static const float ANGLE_WEIGHT = 0.90f;
-    static const float DEEP_CORNER_ANGLE_THRESHOLD = M_PI_F * 60.0f / 180.0f;
-    static const float SKIP_DEEP_CORNER_PROBABILITY = 0.1f;
-    static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 30.0f / 180.0f;
-    static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F * 15.0f / 180.0f;
-    static const float SKIP_CORNER_PROBABILITY = 0.4f;
-    static const float SPEED_MARGIN = 0.1f;
-    static const float CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION = 0.0f;
-
     charProbabilities->resize(sampledInputSize);
     // Calculates probabilities of using a point as a correlated point with the character
     // for each point.
     for (int i = start; i < sampledInputSize; ++i) {
         (*charProbabilities)[i].clear();
-        // First, calculates skip probability. Starts form MIN_SKIP_PROBABILITY.
+        // First, calculates skip probability. Starts from MAX_SKIP_PROBABILITY.
         // Note that all values that are multiplied to this probability should be in [0.0, 1.0];
-        float skipProbability = MAX_SKIP_PROBABILITY;
+        float skipProbability = ProximityInfoParams::MAX_SKIP_PROBABILITY;
 
         const float currentAngle = getPointAngle(sampledInputXs, sampledInputYs, i);
         const float speedRate = (*sampledSpeedRates)[i];
@@ -709,78 +690,74 @@
         }
 
         if (i == 0) {
-            skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT
-                    + NEAREST_DISTANCE_BIAS);
+            skipProbability *= min(1.0f,
+                    nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT
+                            + ProximityInfoParams::NEAREST_DISTANCE_BIAS);
             // Promote the first point
-            skipProbability *= SKIP_FIRST_POINT_PROBABILITY;
+            skipProbability *= ProximityInfoParams::SKIP_FIRST_POINT_PROBABILITY;
         } else if (i == sampledInputSize - 1) {
-            skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT_FOR_LAST
-                    + NEAREST_DISTANCE_BIAS_FOR_LAST);
+            skipProbability *= min(1.0f,
+                    nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT_FOR_LAST
+                            + ProximityInfoParams::NEAREST_DISTANCE_BIAS_FOR_LAST);
             // Promote the last point
-            skipProbability *= SKIP_LAST_POINT_PROBABILITY;
+            skipProbability *= ProximityInfoParams::SKIP_LAST_POINT_PROBABILITY;
         } else {
             // If the current speed is relatively slower than adjacent keys, we promote this point.
-            if ((*sampledSpeedRates)[i - 1] - SPEED_MARGIN > speedRate
-                    && speedRate < (*sampledSpeedRates)[i + 1] - SPEED_MARGIN) {
-                if (currentAngle < CORNER_ANGLE_THRESHOLD) {
+            if ((*sampledSpeedRates)[i - 1] - ProximityInfoParams::SPEED_MARGIN > speedRate
+                    && speedRate
+                            < (*sampledSpeedRates)[i + 1] - ProximityInfoParams::SPEED_MARGIN) {
+                if (currentAngle < ProximityInfoParams::CORNER_ANGLE_THRESHOLD) {
                     skipProbability *= min(1.0f, speedRate
-                            * SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY);
+                            * ProximityInfoParams::SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY);
                 } else {
                     // If the angle is small enough, we promote this point more. (e.g. pit vs put)
-                    skipProbability *= min(1.0f, speedRate * SPEED_WEIGHT_FOR_SKIP_PROBABILITY
-                            + MIN_SPEED_RATE_FOR_SKIP_PROBABILITY);
+                    skipProbability *= min(1.0f,
+                            speedRate * ProximityInfoParams::SPEED_WEIGHT_FOR_SKIP_PROBABILITY
+                                    + ProximityInfoParams::MIN_SPEED_RATE_FOR_SKIP_PROBABILITY);
                 }
             }
 
-            skipProbability *= min(1.0f, speedRate * nearestKeyDistance *
-                    NEAREST_DISTANCE_WEIGHT + NEAREST_DISTANCE_BIAS);
+            skipProbability *= min(1.0f,
+                    speedRate * nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT
+                            + ProximityInfoParams::NEAREST_DISTANCE_BIAS);
 
             // Adjusts skip probability by a rate depending on angle.
             // ANGLE_RATE of skipProbability is adjusted by current angle.
-            skipProbability *= (M_PI_F - currentAngle) / M_PI_F * ANGLE_WEIGHT
-                    + (1.0f - ANGLE_WEIGHT);
-            if (currentAngle > DEEP_CORNER_ANGLE_THRESHOLD) {
-                skipProbability *= SKIP_DEEP_CORNER_PROBABILITY;
+            skipProbability *= (M_PI_F - currentAngle) / M_PI_F * ProximityInfoParams::ANGLE_WEIGHT
+                    + (1.0f - ProximityInfoParams::ANGLE_WEIGHT);
+            if (currentAngle > ProximityInfoParams::DEEP_CORNER_ANGLE_THRESHOLD) {
+                skipProbability *= ProximityInfoParams::SKIP_DEEP_CORNER_PROBABILITY;
             }
             // We assume the angle of this point is the angle for point[i], point[i - 2]
             // and point[i - 3]. The reason why we don't use the angle for point[i], point[i - 1]
             // and point[i - 2] is this angle can be more affected by the noise.
             const float prevAngle = getPointsAngle(sampledInputXs, sampledInputYs, i, i - 2, i - 3);
-            if (i >= 3 && prevAngle < STRAIGHT_ANGLE_THRESHOLD
-                    && currentAngle > CORNER_ANGLE_THRESHOLD) {
-                skipProbability *= SKIP_CORNER_PROBABILITY;
+            if (i >= 3 && prevAngle < ProximityInfoParams::STRAIGHT_ANGLE_THRESHOLD
+                    && currentAngle > ProximityInfoParams::CORNER_ANGLE_THRESHOLD) {
+                skipProbability *= ProximityInfoParams::SKIP_CORNER_PROBABILITY;
             }
         }
 
-        // probabilities must be in [0.0, MAX_SKIP_PROBABILITY];
+        // probabilities must be in [0.0, ProximityInfoParams::MAX_SKIP_PROBABILITY];
         ASSERT(skipProbability >= 0.0f);
-        ASSERT(skipProbability <= MAX_SKIP_PROBABILITY);
+        ASSERT(skipProbability <= ProximityInfoParams::MAX_SKIP_PROBABILITY);
         (*charProbabilities)[i][NOT_AN_INDEX] = skipProbability;
 
         // Second, calculates key probabilities by dividing the rest probability
         // (1.0f - skipProbability).
         const float inputCharProbability = 1.0f - skipProbability;
 
-        // TODO: The variance is critical for accuracy; thus, adjusting these parameter by machine
-        // learning or something would be efficient.
-        static const float SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION = 0.3f;
-        static const float MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION = 0.25f;
-        static const float SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION = 0.5f;
-        static const float MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION = 0.15f;
-        static const float MIN_STANDERD_DIVIATION = 0.37f;
-
         const float speedxAngleRate = min(speedRate * currentAngle / M_PI_F
-                * SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION,
-                        MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION);
+                * ProximityInfoParams::SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION,
+                        ProximityInfoParams::MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION);
         const float speedxNearestKeyDistanceRate = min(speedRate * nearestKeyDistance
-                * SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION,
-                        MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION);
-        const float sigma = speedxAngleRate + speedxNearestKeyDistanceRate + MIN_STANDERD_DIVIATION;
+                * ProximityInfoParams::SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION,
+                        ProximityInfoParams::MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION);
+        const float sigma = speedxAngleRate + speedxNearestKeyDistanceRate
+                + ProximityInfoParams::MIN_STANDERD_DIVIATION;
 
         ProximityInfoUtils::NormalDistribution
-                distribution(CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION, sigma);
-        static const float PREV_DISTANCE_WEIGHT = 0.5f;
-        static const float NEXT_DISTANCE_WEIGHT = 0.6f;
+                distribution(ProximityInfoParams::CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION, sigma);
         // Summing up probability densities of all near keys.
         float sumOfProbabilityDensities = 0.0f;
         for (int j = 0; j < keyCount; ++j) {
@@ -797,8 +774,9 @@
                         // points because the first touch by the user can be sloppy.
                         // So we promote the first point if the distance of that point is larger
                         // than the distance of the next point.
-                        distance = (distance + nextDistance * NEXT_DISTANCE_WEIGHT)
-                                / (1.0f + NEXT_DISTANCE_WEIGHT);
+                        distance = (distance
+                                + nextDistance * ProximityInfoParams::NEXT_DISTANCE_WEIGHT)
+                                        / (1.0f + ProximityInfoParams::NEXT_DISTANCE_WEIGHT);
                     }
                 } else if (i != 0 && i == sampledInputSize - 1) {
                     // For the first point, weighted average of distances from last point and
@@ -810,8 +788,9 @@
                         // because the last touch by the user can be sloppy. So we promote the
                         // last point if the distance of that point is larger than the distance of
                         // the previous point.
-                        distance = (distance + previousDistance * PREV_DISTANCE_WEIGHT)
-                                / (1.0f + PREV_DISTANCE_WEIGHT);
+                        distance = (distance
+                                + previousDistance * ProximityInfoParams::PREV_DISTANCE_WEIGHT)
+                                        / (1.0f + ProximityInfoParams::PREV_DISTANCE_WEIGHT);
                     }
                 }
                 // TODO: Promote the first point when the extended line from the next input is near
@@ -831,8 +810,9 @@
                     const float prevDistance = sqrtf(getPointToKeyByIdLength(
                             maxPointToKeyLength, SampledDistanceCache_G, keyCount, i + 1, j));
                     if (prevDistance < distance) {
-                        distance = (distance + prevDistance * NEXT_DISTANCE_WEIGHT)
-                                / (1.0f + NEXT_DISTANCE_WEIGHT);
+                        distance = (distance
+                                + prevDistance * ProximityInfoParams::NEXT_DISTANCE_WEIGHT)
+                                        / (1.0f + ProximityInfoParams::NEXT_DISTANCE_WEIGHT);
                     }
                 } else if (i != 0 && i == sampledInputSize - 1) {
                     // For the first point, weighted average of distances from last point and
@@ -840,8 +820,9 @@
                     const float prevDistance = sqrtf(getPointToKeyByIdLength(
                             maxPointToKeyLength, SampledDistanceCache_G, keyCount, i - 1, j));
                     if (prevDistance < distance) {
-                        distance = (distance + prevDistance * PREV_DISTANCE_WEIGHT)
-                                / (1.0f + PREV_DISTANCE_WEIGHT);
+                        distance = (distance
+                                + prevDistance * ProximityInfoParams::PREV_DISTANCE_WEIGHT)
+                                        / (1.0f + ProximityInfoParams::PREV_DISTANCE_WEIGHT);
                     }
                 }
                 const float probabilityDensity = distribution.getProbabilityDensity(distance);
@@ -905,7 +886,7 @@
             hash_map_compat<int, float>::iterator it = (*charProbabilities)[i].find(j);
             if (it == (*charProbabilities)[i].end()){
                 (*SampledNearKeysVector)[i].reset(j);
-            } else if(it->second < MIN_PROBABILITY) {
+            } else if(it->second < ProximityInfoParams::MIN_PROBABILITY) {
                 // Erases from near keys vector because it has very low probability.
                 (*SampledNearKeysVector)[i].reset(j);
                 (*charProbabilities)[i].erase(j);
@@ -949,20 +930,14 @@
         std::vector<hash_map_compat<int, float> > *charProbabilities) {
     ASSERT(0 <= index0 && index0 < sampledInputSize);
     ASSERT(0 <= index1 && index1 < sampledInputSize);
-
-    static const float SUPPRESSION_LENGTH_WEIGHT = 1.5f;
-    static const float MIN_SUPPRESSION_RATE = 0.1f;
-    static const float SUPPRESSION_WEIGHT = 0.5f;
-    static const float SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN = 0.1f;
-    static const float SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN = 0.3f;
-
     const float keyWidthFloat = static_cast<float>(mostCommonKeyWidth);
     const float diff = fabsf(static_cast<float>((*lengthCache)[index0] - (*lengthCache)[index1]));
-    if (diff > keyWidthFloat * SUPPRESSION_LENGTH_WEIGHT) {
+    if (diff > keyWidthFloat * ProximityInfoParams::SUPPRESSION_LENGTH_WEIGHT) {
         return false;
     }
-    const float suppressionRate = MIN_SUPPRESSION_RATE
-            + diff / keyWidthFloat / SUPPRESSION_LENGTH_WEIGHT * SUPPRESSION_WEIGHT;
+    const float suppressionRate = ProximityInfoParams::MIN_SUPPRESSION_RATE
+            + diff / keyWidthFloat / ProximityInfoParams::SUPPRESSION_LENGTH_WEIGHT
+                    * ProximityInfoParams::SUPPRESSION_WEIGHT;
     for (hash_map_compat<int, float>::iterator it = (*charProbabilities)[index0].begin();
             it != (*charProbabilities)[index0].end(); ++it) {
         hash_map_compat<int, float>::iterator it2 =  (*charProbabilities)[index1].find(it->first);
@@ -974,9 +949,10 @@
             (*charProbabilities)[index0][NOT_AN_INDEX] += suppression;
 
             // Add the probability of the same key nearby index1
-            const float probabilityGain = min(suppression * SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN,
+            const float probabilityGain = min(suppression
+                    * ProximityInfoParams::SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN,
                     (*charProbabilities)[index1][NOT_AN_INDEX]
-                            * SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN);
+                            * ProximityInfoParams::SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN);
             it2->second += probabilityGain;
             (*charProbabilities)[index1][NOT_AN_INDEX] -= probabilityGain;
         }
@@ -1020,7 +996,6 @@
         int *const codePointBuf) {
     ASSERT(sampledInputSize >= 0);
     memset(codePointBuf, 0, sizeof(codePointBuf[0]) * MAX_WORD_LENGTH);
-    static const float DEMOTION_LOG_PROBABILITY = 0.3f;
     int index = 0;
     float sumLogProbability = 0.0f;
     // TODO: Current implementation is greedy algorithm. DP would be efficient for many cases.
@@ -1030,7 +1005,7 @@
         for (hash_map_compat<int, float>::const_iterator it = (*charProbabilities)[i].begin();
                 it != (*charProbabilities)[i].end(); ++it) {
             const float logProbability = (it->first != NOT_AN_INDEX)
-                    ? it->second + DEMOTION_LOG_PROBABILITY : it->second;
+                    ? it->second + ProximityInfoParams::DEMOTION_LOG_PROBABILITY : it->second;
             if (logProbability < minLogProbability) {
                 minLogProbability = logProbability;
                 character = it->first;
diff --git a/native/jni/src/proximity_info_state_utils.h b/native/jni/src/proximity_info_state_utils.h
index c8f0aeb..af0acc7 100644
--- a/native/jni/src/proximity_info_state_utils.h
+++ b/native/jni/src/proximity_info_state_utils.h
@@ -35,9 +35,8 @@
     static int trimLastTwoTouchPoints(std::vector<int> *sampledInputXs,
             std::vector<int> *sampledInputYs, std::vector<int> *sampledInputTimes,
             std::vector<int> *sampledLengthCache, std::vector<int> *sampledInputIndice);
-    static int updateTouchPoints(const int mostCommonKeyWidth,
-            const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
-            const int *const inputProximities,
+    static int updateTouchPoints(const ProximityInfo *const proximityInfo,
+            const int maxPointToKeyLength, const int *const inputProximities,
             const int *const inputXCoordinates, const int *const inputYCoordinates,
             const int *const times, const int *const pointerIds, const int inputSize,
             const bool isGeometric, const int pointerId, const int pushTouchPointStartIndex,
@@ -65,19 +64,17 @@
             std::vector<int> *beelineSpeedPercentiles);
     static float getDirection(const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs, const int index0, const int index1);
-    static void updateAlignPointProbabilities(
-            const float maxPointToKeyLength, const int mostCommonKeyWidth, const int keyCount,
-            const int start, const int sampledInputSize,
-            const std::vector<int> *const sampledInputXs,
+    static void updateAlignPointProbabilities(const float maxPointToKeyLength,
+            const int mostCommonKeyWidth, const int keyCount, const int start,
+            const int sampledInputSize, const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs,
             const std::vector<float> *const sampledSpeedRates,
             const std::vector<int> *const sampledLengthCache,
             const std::vector<float> *const SampledDistanceCache_G,
             std::vector<NearKeycodesSet> *SampledNearKeysVector,
             std::vector<hash_map_compat<int, float> > *charProbabilities);
-    static void updateSampledSearchKeysVector(
-            const ProximityInfo *const proximityInfo, const int sampledInputSize,
-            const int lastSavedInputSize,
+    static void updateSampledSearchKeysVector(const ProximityInfo *const proximityInfo,
+            const int sampledInputSize, const int lastSavedInputSize,
             const std::vector<int> *const sampledLengthCache,
             const std::vector<NearKeycodesSet> *const SampledNearKeysVector,
             std::vector<NearKeycodesSet> *sampledSearchKeysVector);
@@ -87,22 +84,18 @@
     static float getPointToKeyByIdLength(const float maxPointToKeyLength,
             const std::vector<float> *const SampledDistanceCache_G, const int keyCount,
             const int inputIndex, const int keyId);
-    static void initGeometricDistanceInfos(
-            const ProximityInfo *const proximityInfo, const int keyCount,
+    static void initGeometricDistanceInfos(const ProximityInfo *const proximityInfo,
             const int sampledInputSize, const int lastSavedInputSize,
             const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs,
             std::vector<NearKeycodesSet> *SampledNearKeysVector,
             std::vector<float> *SampledDistanceCache_G);
-    static void initPrimaryInputWord(
-            const int inputSize, const int *const inputProximities, int *primaryInputWord);
-    static void initNormalizedSquaredDistances(
-            const ProximityInfo *const proximityInfo, const int inputSize,
-            const int *inputXCoordinates, const int *inputYCoordinates,
-            const int *const inputProximities,
-            const std::vector<int> *const sampledInputXs,
-            const std::vector<int> *const sampledInputYs,
-            int *normalizedSquaredDistances);
+    static void initPrimaryInputWord(const int inputSize, const int *const inputProximities,
+            int *primaryInputWord);
+    static void initNormalizedSquaredDistances(const ProximityInfo *const proximityInfo,
+            const int inputSize, const int *inputXCoordinates, const int *inputYCoordinates,
+            const int *const inputProximities, const std::vector<int> *const sampledInputXs,
+            const std::vector<int> *const sampledInputYs, int *normalizedSquaredDistances);
     static void dump(const bool isGeometric, const int inputSize,
             const int *const inputXCoordinates, const int *const inputYCoordinates,
             const int sampledInputSize, const std::vector<int> *const sampledInputXs,
@@ -117,8 +110,8 @@
             const std::vector<int> *const sampledTimes,
             const std::vector<int> *const sampledInputIndices);
     // TODO: Move to most_probable_string_utils.h
-    static float getMostProbableString(
-            const ProximityInfo *const proximityInfo, const int sampledInputSize,
+    static float getMostProbableString(const ProximityInfo *const proximityInfo,
+            const int sampledInputSize,
             const std::vector<hash_map_compat<int, float> > *const charProbabilities,
             int *const codePointBuf);
 
@@ -137,11 +130,10 @@
             const NearKeysDistanceMap *const prevNearKeysDistances,
             const NearKeysDistanceMap *const prevPrevNearKeysDistances,
             std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs);
-    static bool pushTouchPoint(const int mostCommonKeyWidth,
-            const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
-            const int inputIndex, const int nodeCodePoint, int x, int y, const int time,
-            const bool doSampling, const bool isLastPoint, const float sumAngle,
-            NearKeysDistanceMap *const currentNearKeysDistances,
+    static bool pushTouchPoint(const ProximityInfo *const proximityInfo,
+            const int maxPointToKeyLength, const int inputIndex, const int nodeCodePoint, int x,
+            int y, const int time, const bool doSampling, const bool isLastPoint,
+            const float sumAngle, NearKeysDistanceMap *const currentNearKeysDistances,
             const NearKeysDistanceMap *const prevNearKeysDistances,
             const NearKeysDistanceMap *const prevPrevNearKeysDistances,
             std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs,
@@ -153,23 +145,20 @@
             const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs,
             const std::vector<int> *const inputIndice);
-    static float getPointAngle(
-            const std::vector<int> *const sampledInputXs,
+    static float getPointAngle(const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs, const int index);
-    static float getPointsAngle(
-            const std::vector<int> *const sampledInputXs,
-            const std::vector<int> *const sampledInputYs,
-            const int index0, const int index1, const int index2);
+    static float getPointsAngle(const std::vector<int> *const sampledInputXs,
+            const std::vector<int> *const sampledInputYs, const int index0, const int index1,
+            const int index2);
     static bool suppressCharProbabilities(const int mostCommonKeyWidth,
-            const int sampledInputSize, const std::vector<int> *const lengthCache,
-            const int index0, const int index1,
-            std::vector<hash_map_compat<int, float> > *charProbabilities);
+            const int sampledInputSize, const std::vector<int> *const lengthCache, const int index0,
+            const int index1, std::vector<hash_map_compat<int, float> > *charProbabilities);
     static float calculateSquaredDistanceFromSweetSpotCenter(
             const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs, const int keyIndex,
             const int inputIndex);
-     static float calculateNormalizedSquaredDistance(
-            const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs,
+     static float calculateNormalizedSquaredDistance(const ProximityInfo *const proximityInfo,
+            const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs, const int keyIndex, const int inputIndex);
 };
 } // namespace latinime
diff --git a/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java b/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java
new file mode 100644
index 0000000..c0eaaea
--- /dev/null
+++ b/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import android.test.AndroidTestCase;
+import android.util.JsonReader;
+
+import com.android.inputmethod.research.MotionEventReader.ReplayData;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+public class MotionEventReaderTests extends AndroidTestCase {
+    private MotionEventReader mMotionEventReader = new MotionEventReader();
+    private ReplayData mReplayData;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mReplayData = new ReplayData();
+    }
+
+    private JsonReader jsonReaderForString(final String s) {
+        return new JsonReader(new StringReader(s));
+    }
+
+    public void testTopLevelDataVariant() {
+        final JsonReader jsonReader = jsonReaderForString(
+                "{"
+                + "\"_ct\": 1359590400000,"
+                + "\"_ut\": 4381933,"
+                + "\"_ty\": \"MotionEvent\","
+                + "\"action\": \"UP\","
+                + "\"isLoggingRelated\": false,"
+                + "\"x\": 100.0,"
+                + "\"y\": 200.0"
+                + "}"
+                );
+        try {
+            mMotionEventReader.readLogStatement(jsonReader, mReplayData);
+        } catch (IOException e) {
+            e.printStackTrace();
+            fail("IOException thrown");
+        }
+        assertEquals("x set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].x, 100);
+        assertEquals("y set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].y, 200);
+        assertEquals("only one pointer", mReplayData.mPointerCoordsArrays.get(0).length, 1);
+        assertEquals("only one MotionEvent", mReplayData.mPointerCoordsArrays.size(), 1);
+    }
+
+    public void testNestedDataVariant() {
+        final JsonReader jsonReader = jsonReaderForString(
+                "{"
+                + "  \"_ct\": 135959040000,"
+                + "  \"_ut\": 4382702,"
+                + "  \"_ty\": \"MotionEvent\","
+                + "  \"action\": \"MOVE\","
+                + "  \"isLoggingRelated\": false,"
+                + "  \"motionEvent\": {"
+                + "    \"pointerIds\": ["
+                + "      0"
+                + "    ],"
+                + "    \"xyt\": ["
+                + "      {"
+                + "        \"t\": 4382551,"
+                + "        \"d\": ["
+                + "          {"
+                + "            \"x\": 100.0,"
+                + "            \"y\": 200.0,"
+                + "            \"toma\": 999.0,"
+                + "            \"tomi\": 999.0,"
+                + "            \"o\": 0.0"
+                + "          }"
+                + "        ]"
+                + "      },"
+                + "      {"
+                + "        \"t\": 4382559,"
+                + "        \"d\": ["
+                + "          {"
+                + "            \"x\": 300.0,"
+                + "            \"y\": 400.0,"
+                + "            \"toma\": 999.0,"
+                + "            \"tomi\": 999.0,"
+                + "            \"o\": 0.0"
+                + "          }"
+                + "        ]"
+                + "      }"
+                + "    ]"
+                + "  }"
+                + "}"
+                );
+        try {
+            mMotionEventReader.readLogStatement(jsonReader, mReplayData);
+        } catch (IOException e) {
+            e.printStackTrace();
+            fail("IOException thrown");
+        }
+        assertEquals("x1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].x, 100);
+        assertEquals("y1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].y, 200);
+        assertEquals("x2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(1)[0].x, 300);
+        assertEquals("y2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(1)[0].y, 400);
+        assertEquals("only one pointer", mReplayData.mPointerCoordsArrays.get(0).length, 1);
+        assertEquals("two MotionEvents", mReplayData.mPointerCoordsArrays.size(), 2);
+    }
+
+    public void testNestedDataVariantMultiPointer() {
+        final JsonReader jsonReader = jsonReaderForString(
+                "{"
+                + "  \"_ct\": 135959040000,"
+                + "  \"_ut\": 4382702,"
+                + "  \"_ty\": \"MotionEvent\","
+                + "  \"action\": \"MOVE\","
+                + "  \"isLoggingRelated\": false,"
+                + "  \"motionEvent\": {"
+                + "    \"pointerIds\": ["
+                + "      1"
+                + "    ],"
+                + "    \"xyt\": ["
+                + "      {"
+                + "        \"t\": 4382551,"
+                + "        \"d\": ["
+                + "          {"
+                + "            \"x\": 100.0,"
+                + "            \"y\": 200.0,"
+                + "            \"toma\": 999.0,"
+                + "            \"tomi\": 999.0,"
+                + "            \"o\": 0.0"
+                + "          },"
+                + "          {"
+                + "            \"x\": 300.0,"
+                + "            \"y\": 400.0,"
+                + "            \"toma\": 999.0,"
+                + "            \"tomi\": 999.0,"
+                + "            \"o\": 0.0"
+                + "          }"
+                + "        ]"
+                + "      }"
+                + "    ]"
+                + "  }"
+                + "}"
+                );
+        try {
+            mMotionEventReader.readLogStatement(jsonReader, mReplayData);
+        } catch (IOException e) {
+            e.printStackTrace();
+            fail("IOException thrown");
+        }
+        assertEquals("x1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].x, 100);
+        assertEquals("y1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].y, 200);
+        assertEquals("x2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[1].x, 300);
+        assertEquals("y2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[1].y, 400);
+        assertEquals("two pointers", mReplayData.mPointerCoordsArrays.get(0).length, 2);
+        assertEquals("one MotionEvent", mReplayData.mPointerCoordsArrays.size(), 1);
+    }
+}