Opensource a test tool called EditTextVariations

This CL opensouces a testing tool called EditTextVariations that has
been used internally to test IME behaviors on various
EditorInfo#inputType and EditorInfo#imeOptions.

Bug: 80039502
Test: Manually verified as follows.
 1. tapas EditTextVariations
 2. make -j
 3. adb install -r $ANDROID_TARGET_OUT_TESTCASES/EditTextVariations/EditTextVariations.apk
Change-Id: Ia87e655573c2fd1fc09d56e4af90bfb1dfd65f9b
diff --git a/tools/EditTextVariations/Android.mk b/tools/EditTextVariations/Android.mk
new file mode 100644
index 0000000..1f49aa4
--- /dev/null
+++ b/tools/EditTextVariations/Android.mk
@@ -0,0 +1,26 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := EditTextVariations
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/tools/EditTextVariations/AndroidManifest.xml b/tools/EditTextVariations/AndroidManifest.xml
new file mode 100644
index 0000000..7694f4d
--- /dev/null
+++ b/tools/EditTextVariations/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.inputmethod.tools.edittextvariations"
+    android:versionName="0.67"
+    android:versionCode="67"
+>
+    <supports-screens android:resizeable="true" />
+    <uses-sdk
+        android:targetSdkVersion="27"
+        android:minSdkVersion="11" />
+    <application
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:allowBackup="false"
+    >
+        <activity
+            android:name=".EditTextVariations"
+            android:windowSoftInputMode="stateHidden|adjustPan"
+            android:theme="@style/defaultActivityTheme"
+            android:label="@string/app_name"
+        >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tools/EditTextVariations/assets/Theme.css b/tools/EditTextVariations/assets/Theme.css
new file mode 100644
index 0000000..b7aa514
--- /dev/null
+++ b/tools/EditTextVariations/assets/Theme.css
@@ -0,0 +1,56 @@
+<!--
+/*
+**
+** Copyright 2014, 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.
+*/
+-->
+
+body {
+    background-color: black;
+}
+div.placeholder {
+    color: #a0a0a0;
+    font-size: 18px;
+    margin: 0px 0px -34px 0px;
+    padding: 4px 0px 4px 0px
+}
+div.input {
+    border: none;
+    margin: 4px 0px 4px -8px;
+}
+input {
+    color: black;
+    background-color: white;
+    font-size: 18px;
+    line-height: 200%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: none;
+    border-left: none;
+}
+textarea {
+    color: black;
+    background-color: white;
+    font-size: 18px;
+    line-height: 150%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: none;
+    border-left: none;
+}
diff --git a/tools/EditTextVariations/assets/Theme_Black.css b/tools/EditTextVariations/assets/Theme_Black.css
new file mode 100644
index 0000000..ce663a1
--- /dev/null
+++ b/tools/EditTextVariations/assets/Theme_Black.css
@@ -0,0 +1,56 @@
+<!--
+/*
+**
+** Copyright 2011, 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.
+*/
+-->
+
+body {
+    background-color: black;
+}
+div.placeholder {
+    color: #a0a0a0;
+    font-size: 18px;
+    margin: 0px 0px -34px 0px;
+    padding: 4px 0px 4px 0px
+}
+div.input {
+    border: none;
+    margin: 4px 0px 4px -8px;
+}
+input {
+    color: black;
+    background-color: white;
+    font-size: 18px;
+    line-height: 200%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: none;
+    border-left: none;
+}
+textarea {
+    color: black;
+    background-color: white;
+    font-size: 18px;
+    line-height: 150%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: none;
+    border-left: none;
+}
diff --git a/tools/EditTextVariations/assets/Theme_Holo.css b/tools/EditTextVariations/assets/Theme_Holo.css
new file mode 100644
index 0000000..a3afe2e
--- /dev/null
+++ b/tools/EditTextVariations/assets/Theme_Holo.css
@@ -0,0 +1,56 @@
+<!--
+/*
+**
+** Copyright 2011, 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.
+*/
+-->
+
+body {
+    background-color: #16191C;
+}
+div.placeholder {
+    color: #808080;
+    font-size: 18px;
+    margin: 0px 0px -34px 0px;
+    padding: 4px 0px 4px 0px
+}
+div.input {
+    border: none;
+    margin: 4px 0px 4px -8px;
+}
+input {
+    color: white;
+    background-color: rgba(0, 0, 0, 0.0);
+    font-size: 18px;
+    line-height: 180%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: 1px solid #5E6063;
+    border-left: none;
+}
+textarea {
+    color: white;
+    background-color: rgba(0, 0, 0, 0.0);
+    font-size: 18px;
+    line-height: 150%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: 1px solid #5E6063;
+    border-left: none;
+}
diff --git a/tools/EditTextVariations/assets/Theme_Holo_Light.css b/tools/EditTextVariations/assets/Theme_Holo_Light.css
new file mode 100644
index 0000000..fb3a83c
--- /dev/null
+++ b/tools/EditTextVariations/assets/Theme_Holo_Light.css
@@ -0,0 +1,56 @@
+<!--
+/*
+**
+** Copyright 2011, 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.
+*/
+-->
+
+body {
+    background-color: #F2F2F2;
+}
+div.placeholder {
+    color: #808080;
+    font-size: 18px;
+    margin: 0px 0px -34px 0px;
+    padding: 4px 0px 4px 0px
+}
+div.input {
+    border: none;
+    margin: 4px 0px 4px -8px;
+}
+input {
+    color: black;
+    background-color: rgba(0, 0, 0, 0.0);
+    font-size: 18px;
+    line-height: 150%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: 1px solid #808080;
+    border-left: none;
+}
+textarea {
+    color: black;
+    background-color: rgba(0, 0, 0, 0.0);
+    font-size: 18px;
+    line-height: 150%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: 1px solid #808080;
+    border-left: none;
+}
diff --git a/tools/EditTextVariations/assets/Theme_Light.css b/tools/EditTextVariations/assets/Theme_Light.css
new file mode 100644
index 0000000..4cce0a0
--- /dev/null
+++ b/tools/EditTextVariations/assets/Theme_Light.css
@@ -0,0 +1,56 @@
+<!--
+/*
+**
+** Copyright 2011, 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.
+*/
+-->
+
+body {
+    background-color: #f3f3f3;
+}
+div.placeholder {
+    color: #a0a0a0;
+    font-size: 18px;
+    margin: 0px 0px -34px 0px;
+    padding: 4px 0px 4px 0px
+}
+div.input {
+    border: none;
+    margin: 4px 0px 4px -8px;
+}
+input {
+    color: black;
+    background-color: rgba(0, 0, 0, 0.0);
+    font-size: 18px;
+    line-height: 200%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: 1px solid #808080;
+    border-right: 1px solid rgba(0, 0, 0, 0.5);
+    border-bottom: 1px solid #808080;
+    border-left: 1px solid rgba(0, 0, 0, 0.5);
+}
+textarea {
+    color: black;
+    background-color: rgba(0, 0, 0, 0.0);
+    font-size: 18px;
+    line-height: 150%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: 1px solid #808080;
+    border-right: 1px solid rgba(0, 0, 0, 0.5);
+    border-bottom: 1px solid #808080;
+    border-left: 1px solid rgba(0, 0, 0, 0.5);
+}
diff --git a/tools/EditTextVariations/assets/Theme_Material.css b/tools/EditTextVariations/assets/Theme_Material.css
new file mode 100644
index 0000000..3581a88
--- /dev/null
+++ b/tools/EditTextVariations/assets/Theme_Material.css
@@ -0,0 +1,56 @@
+<!--
+/*
+**
+** Copyright 2014, 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.
+*/
+-->
+
+body {
+    background-color: #212121;
+}
+div.placeholder {
+    color: #909090;
+    font-size: 18px;
+    margin: 0px 0px -34px 0px;
+    padding: 4px 0px 4px 0px
+}
+div.input {
+    border: none;
+    margin: 4px 0px 4px -8px;
+}
+input {
+    color: white;
+    background-color: rgba(0, 0, 0, 0.0);
+    font-size: 18px;
+    line-height: 180%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: 1px solid #757575;
+    border-left: none;
+}
+textarea {
+    color: white;
+    background-color: rgba(0, 0, 0, 0.0);
+    font-size: 18px;
+    line-height: 150%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: 1px solid #757575;
+    border-left: none;
+}
diff --git a/tools/EditTextVariations/assets/Theme_Material_Light.css b/tools/EditTextVariations/assets/Theme_Material_Light.css
new file mode 100644
index 0000000..1a78a2d
--- /dev/null
+++ b/tools/EditTextVariations/assets/Theme_Material_Light.css
@@ -0,0 +1,56 @@
+<!--
+/*
+**
+** Copyright 2014, 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.
+*/
+-->
+
+body {
+    background-color: #FAFAFA;
+}
+div.placeholder {
+    color: #7D7D7D;
+    font-size: 18px;
+    margin: 0px 0px -34px 0px;
+    padding: 4px 0px 4px 0px
+}
+div.input {
+    border: none;
+    margin: 4px 0px 4px -8px;
+}
+input {
+    color: white;
+    background-color: rgba(0, 0, 0, 0.0);
+    font-size: 18px;
+    line-height: 180%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: 1px solid #737373;
+    border-left: none;
+}
+textarea {
+    color: white;
+    background-color: rgba(0, 0, 0, 0.0);
+    font-size: 18px;
+    line-height: 150%;
+    vertical-align: center;
+    padding-left: 8px;
+    border-top: none;
+    border-right: none;
+    border-bottom: 1px solid #737373;
+    border-left: none;
+}
diff --git a/tools/EditTextVariations/assets/webview.html b/tools/EditTextVariations/assets/webview.html
new file mode 100644
index 0000000..daf0fdb
--- /dev/null
+++ b/tools/EditTextVariations/assets/webview.html
@@ -0,0 +1,96 @@
+<!--
+/*
+**
+** Copyright 2011, 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.
+*/
+-->
+
+<html>
+    <head>
+        <script type="text/javascript">
+            (function(d, name) {
+                var css = document.createElement('link');
+                css.rel = 'stylesheet';
+                css.href = name + ".css";
+                d.head.appendChild(css);
+            })(document, theme.name());
+        </script>
+    </head>
+    <body>
+    <div
+        class="input"
+        style="margin-top:-8px"
+    >
+        <input
+            type="text"
+            size="80%"
+            name="webtext"
+            placeholder="&lt;input type=&quot;text&quot;/&gt;" />
+    </div>
+    <div class="input">
+        <input
+            type="email"
+            size="80%"
+            name="webemail"
+            placeholder="&lt;input type=&quot;email&quot;/&gt;" />
+    </div>
+    <div class="input">
+        <input
+            type="password"
+            size="80%"
+            name="webpassword"
+            placeholder="&lt;input type=&quot;password&quot;/&gt;" />
+    </div>
+    <div class="input">
+        <input
+            type="url"
+            size="80%"
+            name="weburl"
+            placeholder="&lt;input type=&quot;url&quot;/&gt;" />
+    </div>
+    <div class="input">
+        <input
+            type="number"
+            size="80%"
+            name="webnumber"
+            placeholder="&lt;input type=&quot;number&quot;/&gt;" />
+    </div>
+    <div class="input">
+        <input
+            type="tel"
+            size="80%"
+            name="webtel"
+            placeholder="&lt;input type=&quot;tel&quot;/&gt;" />
+    </div>
+    <div class="input">
+        <input
+            type="search"
+            size="80%"
+            name="websearch"
+            placeholder="&lt;input type=&quot;search&quot;/&gt;" />
+    </div>
+    <div
+        class="input"
+        style="margin-bottom:-4px"
+    >
+        <textarea
+            rows="2"
+            cols="80%"
+            name="webtextarea"
+            placeholder="&lt;textarea&gt;&lt/textarea&gt;"
+        ></textarea>
+    </div>
+  </body>
+</html>
diff --git a/tools/EditTextVariations/res/drawable-hdpi/ic_launcher.png b/tools/EditTextVariations/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..e0923a6
--- /dev/null
+++ b/tools/EditTextVariations/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/tools/EditTextVariations/res/drawable-mdpi/ic_launcher.png b/tools/EditTextVariations/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..9bc0914
--- /dev/null
+++ b/tools/EditTextVariations/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/tools/EditTextVariations/res/drawable-xhdpi/ic_launcher.png b/tools/EditTextVariations/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..d60ac3d
--- /dev/null
+++ b/tools/EditTextVariations/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/tools/EditTextVariations/res/drawable-xxhdpi/ic_launcher.png b/tools/EditTextVariations/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..f3069d7
--- /dev/null
+++ b/tools/EditTextVariations/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/tools/EditTextVariations/res/layout/main.xml b/tools/EditTextVariations/res/layout/main.xml
new file mode 100644
index 0000000..99abfb0
--- /dev/null
+++ b/tools/EditTextVariations/res/layout/main.xml
@@ -0,0 +1,298 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+>
+    <LinearLayout
+        android:id="@+id/edit_text_list"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+    >
+        <EditText
+            android:id="@+id/text_multi_lines_none"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textMultiLine|textAutoCorrect"
+            android:imeOptions="actionUnspecified" />
+        <EditText
+            android:id="@+id/text_multi_lines_send"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textMultiLine|textAutoCorrect"
+            android:imeOptions="actionSend" />
+        <view
+            class="com.android.inputmethod.tools.edittextvariations.MultiLineShortMessageEditText"
+            android:id="@+id/text_short_message_send_multi_lines"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textShortMessage|textMultiLine|textAutoCorrect|textCapSentences"
+            android:imeOptions="actionSend" />
+        <view
+            class="com.android.inputmethod.tools.edittextvariations.MultiLineShortMessageEditText"
+            android:id="@+id/text_multi_lines_search"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textMultiLine|textAutoCorrect|textCapSentences"
+            android:imeOptions="actionSearch" />
+        <EditText
+            android:id="@+id/text_short_message_send"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
+            android:imeOptions="actionSend" />
+        <EditText
+            android:id="@+id/text_autocap_none"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textAutoCorrect|textCapSentences"
+            android:imeOptions="actionNone" />
+        <EditText
+            android:id="@+id/text_autocap_send"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textAutoCorrect|textCapSentences"
+            android:imeOptions="actionSend" />
+        <EditText
+            android:id="@+id/text_uri_go"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textUri"
+            android:imeOptions="actionGo" />
+        <EditText
+            android:id="@+id/text_email_address_done"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textEmailAddress"
+            android:imeOptions="actionDone" />
+        <EditText
+            android:id="@+id/text_auto_correct_search"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textAutoCorrect"
+            android:imeOptions="actionSearch" />
+        <EditText
+            android:id="@+id/text_auto_correct_next"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textAutoCorrect"
+            android:imeOptions="actionNext" />
+        <EditText
+            android:id="@+id/text_auto_correct_previous"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textAutoCorrect" />
+        <EditText
+            android:id="@+id/text_auto_correct_custom"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textAutoCorrect"
+            android:imeActionLabel="@string/custom_action_label"
+            android:imeActionId="100" />
+        <EditText
+            android:id="@+id/phone"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="phone" />
+        <EditText
+            android:id="@+id/phone_no_action"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="phone"
+            android:imeOptions="actionNone" />
+        <EditText
+            android:id="@+id/number_send"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="number"
+            android:imeOptions="actionSend" />
+        <EditText
+            android:id="@+id/number_no_action"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="number"
+            android:imeOptions="actionNone" />
+        <EditText
+            android:id="@+id/text_password_next"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textPassword"
+            android:imeOptions="actionNext" />
+        <EditText
+            android:id="@+id/text_visible_password_done"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textVisiblePassword"
+            android:imeOptions="actionDone" />
+        <EditText
+            android:id="@+id/number_password_send"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="numberPassword"
+            android:imeOptions="actionDone" />
+        <EditText
+            android:id="@+id/text_no_suggestions"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textNoSuggestions"
+            android:imeOptions="actionGo" />
+        <EditText
+            android:id="@+id/text_no_auto_correction"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textShortMessage"
+            android:imeOptions="actionDone" />
+        <WebView
+            android:id="@+id/web_view"
+            android:layout_width="fill_parent"
+            android:layout_height="380sp"
+            android:focusable="true" />
+        <EditText
+            android:id="@+id/text_cap_characters_with_auto_correction"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textCapCharacters|textAutoCorrect" />
+        <EditText
+            android:id="@+id/text_cap_words_with_auto_correction"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textCapWords|textAutoCorrect" />
+        <EditText
+            android:id="@+id/text_cap_sentences_with_auto_correction"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textCapSentences|textAutoCorrect" />
+        <EditText
+            android:id="@+id/text_cap_characters"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textCapCharacters" />
+        <EditText
+            android:id="@+id/text_cap_words"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textCapWords" />
+        <EditText
+            android:id="@+id/text_cap_sentences"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textCapSentences" />
+        <EditText
+            android:id="@+id/text_email_subject"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textEmailSubject" />
+        <EditText
+            android:id="@+id/text_personal_name"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textPersonName" />
+        <EditText
+            android:id="@+id/text_postal_address"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textPostalAddress" />
+        <EditText
+            android:id="@+id/text_phonetic"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textPhonetic" />
+        <EditText
+            android:id="@+id/number_signed"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="numberSigned" />
+        <EditText
+            android:id="@+id/number_decimal"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="numberDecimal" />
+        <EditText
+            android:id="@+id/number_signed_decimal"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="numberSigned|numberDecimal" />
+        <EditText
+            android:id="@+id/date_time"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="datetime" />
+        <EditText
+            android:id="@+id/date"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="date" />
+        <EditText
+            android:id="@+id/time"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="time" />
+        <EditText
+            android:id="@+id/text_no_extract_ui"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="text"
+            android:imeOptions="flagNoExtractUi" />
+        <EditText
+            android:id="@+id/text_no_fullscreen"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="textCapSentences"
+            android:imeOptions="flagNoFullscreen" />
+        <EditText
+            android:id="@+id/text_nm"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="text"
+            android:privateImeOptions="nm" />
+        <EditText
+            android:id="@+id/text_force_ascii_flag"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="text" />
+        <AutoCompleteTextView
+            android:id="@+id/text_app_completion"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content" />
+        <AutoCompleteTextView
+            android:id="@+id/text_app_completion_no_fullscreen"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:imeOptions="flagNoFullscreen" />
+        <AutoCompleteTextView
+            android:id="@+id/text_app_compeletion_no_extract_ui"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:imeOptions="flagNoExtractUi" />
+        <EditText
+            android:id="@+id/text_restarting"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="text" />
+        <EditText
+            android:id="@+id/text_null"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:inputType="none" />
+    </LinearLayout>
+</ScrollView>
diff --git a/tools/EditTextVariations/res/values-v11/donottranslate.xml b/tools/EditTextVariations/res/values-v11/donottranslate.xml
new file mode 100644
index 0000000..5b7eb23
--- /dev/null
+++ b/tools/EditTextVariations/res/values-v11/donottranslate.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, 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.
+*/
+-->
+
+<resources>
+    <string name="default_theme">Theme_Holo_Light</string>
+</resources>
diff --git a/tools/EditTextVariations/res/values-v14/themes.xml b/tools/EditTextVariations/res/values-v14/themes.xml
new file mode 100644
index 0000000..22c6016
--- /dev/null
+++ b/tools/EditTextVariations/res/values-v14/themes.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2017, 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.
+*/
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="defaultActivityTheme" parent="@android:style/Theme.DeviceDefault.Light" />
+</resources>
diff --git a/tools/EditTextVariations/res/values-v27/themes.xml b/tools/EditTextVariations/res/values-v27/themes.xml
new file mode 100644
index 0000000..8fd3aae
--- /dev/null
+++ b/tools/EditTextVariations/res/values-v27/themes.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2017, 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.
+*/
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="defaultActivityTheme" parent="@android:style/Theme.DeviceDefault.Light">
+        <item name="android:navigationBarColor">@android:color/white</item>
+        <item name="android:navigationBarDividerColor">#1f000000</item>
+        <item name="android:windowLightNavigationBar">true</item>
+    </style>
+</resources>
diff --git a/tools/EditTextVariations/res/values/countries.xml b/tools/EditTextVariations/res/values/countries.xml
new file mode 100644
index 0000000..6d7e5c1
--- /dev/null
+++ b/tools/EditTextVariations/res/values/countries.xml
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<resources>
+    <!-- The list of country names. [CHAR LIMIT=100] -->
+    <string-array name="countries_array">
+        <item>Afghanistan</item>
+        <item>Albania</item>
+        <item>Algeria</item>
+        <item>American Samoa</item>
+        <item>Andorra</item>
+        <item>Angola</item>
+        <item>Anguilla</item>
+        <item>Antarctica</item>
+        <item>Antigua and Barbuda</item>
+        <item>Argentina</item>
+        <item>Armenia</item>
+        <item>Aruba</item>
+        <item>Australia</item>
+        <item>Austria</item>
+        <item>Azerbaijan</item>
+        <item>Bahrain</item>
+        <item>Bangladesh</item>
+        <item>Barbados</item>
+        <item>Belarus</item>
+        <item>Belgium</item>
+        <item>Belize</item>
+        <item>Benin</item>
+        <item>Bermuda</item>
+        <item>Bhutan</item>
+        <item>Bolivia</item>
+        <item>Bosnia and Herzegovina</item>
+        <item>Botswana</item>
+        <item>Bouvet Island</item>
+        <item>Brazil</item>
+        <item>British Indian Ocean Territory</item>
+        <item>British Virgin Islands</item>
+        <item>Brunei</item>
+        <item>Bulgaria</item>
+        <item>Burkina Faso</item>
+        <item>Burundi</item>
+        <item>"Cote d'Ivoire"</item>
+        <item>Cambodia</item>
+        <item>Cameroon</item>
+        <item>Canada</item>
+        <item>Cape Verde</item>
+        <item>Cayman Islands</item>
+        <item>Central African Republic</item>
+        <item>Chad</item>
+        <item>Chile</item>
+        <item>China</item>
+        <item>Christmas Island</item>
+        <item>Cocos (Keeling) Islands</item>
+        <item>Colombia</item>
+        <item>Comoros</item>
+        <item>Congo</item>
+        <item>Cook Islands</item>
+        <item>Costa Rica</item>
+        <item>Croatia</item>
+        <item>Cuba</item>
+        <item>Cyprus</item>
+        <item>Czech Republic</item>
+        <item>Democratic Republic of the Congo</item>
+        <item>Denmark</item>
+        <item>Djibouti</item>
+        <item>Dominica</item>
+        <item>Dominican Republic</item>
+        <item>East Timor</item>
+        <item>Ecuador</item>
+        <item>Egypt</item>
+        <item>El Salvador</item>
+        <item>Equatorial Guinea</item>
+        <item>Eritrea</item>
+        <item>Estonia</item>
+        <item>Ethiopia</item>
+        <item>Faeroe Islands</item>
+        <item>Falkland Islands</item>
+        <item>Fiji</item>
+        <item>Finland</item>
+        <item>Republic of Macedonia</item>
+        <item>France</item>
+        <item>French Guiana</item>
+        <item>French Polynesia</item>
+        <item>French Southern Territories</item>
+        <item>Gabon</item>
+        <item>Georgia</item>
+        <item>Germany</item>
+        <item>Ghana</item>
+        <item>Gibraltar</item>
+        <item>Greece</item>
+        <item>Greenland</item>
+        <item>Grenada</item>
+        <item>Guadeloupe</item>
+        <item>Guam</item>
+        <item>Guatemala</item>
+        <item>Guinea</item>
+        <item>Guinea-Bissau</item>
+        <item>Guyana</item>
+        <item>Haiti</item>
+        <item>Heard Island and McDonald Islands</item>
+        <item>Honduras</item>
+        <item>Hong Kong</item>
+        <item>Hungary</item>
+        <item>Iceland</item>
+        <item>India</item>
+        <item>Indonesia</item>
+        <item>Iran</item>
+        <item>Iraq</item>
+        <item>Ireland</item>
+        <item>Israel</item>
+        <item>Italy</item>
+        <item>Jamaica</item>
+        <item>Japan</item>
+        <item>Jordan</item>
+        <item>Kazakhstan</item>
+        <item>Kenya</item>
+        <item>Kiribati</item>
+        <item>Kuwait</item>
+        <item>Kyrgyzstan</item>
+        <item>Laos</item>
+        <item>Latvia</item>
+        <item>Lebanon</item>
+        <item>Lesotho</item>
+        <item>Liberia</item>
+        <item>Libya</item>
+        <item>Liechtenstein</item>
+        <item>Lithuania</item>
+        <item>Luxembourg</item>
+        <item>Macau</item>
+        <item>Madagascar</item>
+        <item>Malawi</item>
+        <item>Malaysia</item>
+        <item>Maldives</item>
+        <item>Mali</item>
+        <item>Malta</item>
+        <item>Marshall Islands</item>
+        <item>Martinique</item>
+        <item>Mauritania</item>
+        <item>Mauritius</item>
+        <item>Mayotte</item>
+        <item>Mexico</item>
+        <item>Micronesia</item>
+        <item>Moldova</item>
+        <item>Monaco</item>
+        <item>Mongolia</item>
+        <item>Montserrat</item>
+        <item>Morocco</item>
+        <item>Mozambique</item>
+        <item>Myanmar</item>
+        <item>Namibia</item>
+        <item>Nauru</item>
+        <item>Nepal</item>
+        <item>Netherlands</item>
+        <item>Netherlands Antilles</item>
+        <item>New Caledonia</item>
+        <item>New Zealand</item>
+        <item>Nicaragua</item>
+        <item>Niger</item>
+        <item>Nigeria</item>
+        <item>Niue</item>
+        <item>Norfolk Island</item>
+        <item>North Korea</item>
+        <item>Northern Marianas</item>
+        <item>Norway</item>
+        <item>Oman</item>
+        <item>Pakistan</item>
+        <item>Palau</item>
+        <item>Panama</item>
+        <item>Papua New Guinea</item>
+        <item>Paraguay</item>
+        <item>Peru</item>
+        <item>Philippines</item>
+        <item>Pitcairn Islands</item>
+        <item>Poland</item>
+        <item>Portugal</item>
+        <item>Puerto Rico</item>
+        <item>Qatar</item>
+        <item>Reunion</item>
+        <item>Romania</item>
+        <item>Russia</item>
+        <item>Rwanda</item>
+        <item>Sao Tome and Principe</item>
+        <item>Saint Helena</item>
+        <item>Saint Kitts and Nevis</item>
+        <item>Saint Lucia</item>
+        <item>Saint Pierre and Miquelon</item>
+        <item>Saint Vincent and the Grenadines</item>
+        <item>Samoa</item>
+        <item>San Marino</item>
+        <item>Saudi Arabia</item>
+        <item>Senegal</item>
+        <item>Seychelles</item>
+        <item>Sierra Leone</item>
+        <item>Singapore</item>
+        <item>Slovakia</item>
+        <item>Slovenia</item>
+        <item>Solomon Islands</item>
+        <item>Somalia</item>
+        <item>South Africa</item>
+        <item>South Georgia and the South Sandwich Islands</item>
+        <item>South Korea</item>
+        <item>Spain</item>
+        <item>Sri Lanka</item>
+        <item>Sudan</item>
+        <item>Suriname</item>
+        <item>Svalbard and Jan Mayen</item>
+        <item>Swaziland</item>
+        <item>Sweden</item>
+        <item>Switzerland</item>
+        <item>Syria</item>
+        <item>Taiwan</item>
+        <item>Tajikistan</item>
+        <item>Tanzania</item>
+        <item>Thailand</item>
+        <item>The Bahamas</item>
+        <item>The Gambia</item>
+        <item>Togo</item>
+        <item>Tokelau</item>
+        <item>Tonga</item>
+        <item>Trinidad and Tobago</item>
+        <item>Tunisia</item>
+        <item>Turkey</item>
+        <item>Turkmenistan</item>
+        <item>Turks and Caicos Islands</item>
+        <item>Tuvalu</item>
+        <item>Virgin Islands</item>
+        <item>Uganda</item>
+        <item>Ukraine</item>
+        <item>United Arab Emirates</item>
+        <item>United Kingdom</item>
+        <item>United States</item>
+        <item>United States Minor Outlying Islands</item>
+        <item>Uruguay</item>
+        <item>Uzbekistan</item>
+        <item>Vanuatu</item>
+        <item>Vatican City</item>
+        <item>Venezuela</item>
+        <item>Vietnam</item>
+        <item>Wallis and Futuna</item>
+        <item>Western Sahara</item>
+        <item>Yemen</item>
+        <item>Zambia</item>
+        <item>Zimbabwe</item>
+        <item>Yemen</item>
+    </string-array>
+</resources>
diff --git a/tools/EditTextVariations/res/values/strings.xml b/tools/EditTextVariations/res/values/strings.xml
new file mode 100644
index 0000000..02387f2
--- /dev/null
+++ b/tools/EditTextVariations/res/values/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- This test application name. -->
+    <string name="app_name" translatable="false">EditTextVariations</string>
+    <!-- The menu title to change color theme of this application. [CHAR LIMIT=20] -->
+    <string name="menu_change_theme">Change Theme</string>
+    <!-- The menu title to display the version name of this application. [CHAR LIMIT=20] -->
+    <string name="menu_version">Version <xliff:g id="VERSION_NAME" example="0.56">%s</xliff:g></string>
+    <!-- The menu title to turn on the text field focus navigation. [CHAR LIMIT=20] -->
+    <string name="menu_navigate_on" translatable="false">Navigate On</string>
+    <!-- The menu title to turn off the text field focus navigation. [CHAR LIMIT=20] -->
+    <string name="menu_navigate_off" translatable="false">Navigate Off</string>
+    <!-- The menu title to show software keyboard when the application is launched. [CHAR LIMIT=20] -->
+    <string name="menu_softinput_visible" translatable="false">Keyboard Visible</string>
+    <!-- The menu title to stay hidden software keyboard when the application is launched. [CHAR LIMIT=20] -->
+    <string name="menu_softinput_hidden" translatable="false">Keyboard Hidden</string>
+    <!-- The example of custom action key label. Must be short to fit on key. 5 chars or less is preferable.  [CHAR LIMIT=7] -->
+    <string name="custom_action_label">Custom</string>
+</resources>
diff --git a/tools/EditTextVariations/res/values/themes.xml b/tools/EditTextVariations/res/values/themes.xml
new file mode 100644
index 0000000..036a711
--- /dev/null
+++ b/tools/EditTextVariations/res/values/themes.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2017, 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.
+*/
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="defaultActivityTheme" parent="@android:style/Theme.Holo" />
+</resources>
diff --git a/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/EchoingTextWatcher.java b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/EchoingTextWatcher.java
new file mode 100644
index 0000000..1c652c2
--- /dev/null
+++ b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/EchoingTextWatcher.java
@@ -0,0 +1,86 @@
+/*
+ * 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.tools.edittextvariations;
+
+import android.annotation.SuppressLint;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.widget.EditText;
+
+import java.util.Locale;
+
+final class EchoingTextWatcher implements TextWatcher {
+    private static final int SET_TEXT_DELAY = 500;
+
+    final EditText mEditText;
+    CharSequence mExpected;
+
+    @SuppressLint("HandlerLeak")
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(final Message msg) {
+            final String toBeappended = (String) msg.obj;
+            final CharSequence current = mEditText.getText();
+            final CharSequence newText = TextUtils.concat(current, toBeappended);
+            mExpected = newText;
+            mEditText.setText(newText);
+            mEditText.setSelection(newText.length());
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public static void attachTo(final EditText editText) {
+        final EchoingTextWatcher watcher = new EchoingTextWatcher(editText);
+    }
+
+    public EchoingTextWatcher(final EditText editText) {
+        mEditText = editText;
+        editText.addTextChangedListener(this);
+    }
+
+    @Override
+    public void afterTextChanged(final Editable ss) {
+    }
+
+    @Override
+    public void beforeTextChanged(final CharSequence s, final int start, final int count,
+            final int after) {
+    }
+
+    @Override
+    public void onTextChanged(final CharSequence s, final int start, final int before,
+            final int count) {
+        if (count == 0 || before > 0 || TextUtils.equals(s, mExpected)) {
+            return;
+        }
+        final int len = s.length();
+        if (len > 0) {
+            final String last = s.subSequence(len - 1, len).toString();
+            final char lastChar = last.charAt(0);
+            if (Character.isUpperCase(lastChar)) {
+                final String lowerCase = last.toLowerCase(Locale.getDefault());
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(0, lowerCase), SET_TEXT_DELAY);
+            } else if (Character.isLowerCase(lastChar)) {
+                final String upperCase = last.toUpperCase(Locale.getDefault());
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(0, upperCase), SET_TEXT_DELAY);
+            }
+        }
+    }
+}
diff --git a/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/EditTextVariations.java b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/EditTextVariations.java
new file mode 100644
index 0000000..44e0a4d
--- /dev/null
+++ b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/EditTextVariations.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2010 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.tools.edittextvariations;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.EditorInfo;
+import android.webkit.JavascriptInterface;
+import android.webkit.WebView;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class EditTextVariations extends Activity implements TextView.OnEditorActionListener,
+        DialogInterface.OnClickListener {
+    private static final String TAG = EditTextVariations.class.getSimpleName();
+    private static final boolean DEBUG_INPUT_TEXT = false;
+
+    private static final int MENU_CHANGE_THEME = 0;
+    private static final int MENU_VERSION = 1;
+    private static final int MENU_NAVIGATE_ON = 2;
+    private static final int MENU_NAVIGATE_OFF = 3;
+    private static final int MENU_SOFTINPUT_VISIBLE = 4;
+    private static final int MENU_SOFTINPUT_HIDDEN = 5;
+    private static final String PREF_THEME = "theme";
+    private static final String PREF_NAVIGATE = "navigate";
+    private static final String PREF_SOFTINPUT = "softinput";
+
+    private SharedPreferences prefs;
+    private View[] fields;
+
+    private static final FinalClassField<Integer> ApplicationInfo_FLAG_SUPPORTS_RTL =
+            FinalClassField.newInstance(ApplicationInfo.class, "FLAG_SUPPORTS_RTL", 1 << 22);
+
+    // This flag should be defined IceCreamSandwich and later.
+    // Note that Froyo and Gingerbread have hidden IME_FLAG_NO_FULLSCREEN as
+    // value 0x80000000.
+    private static final FinalClassField<Integer> EditorInfo_IME_FLAG_FORCE_ASCII =
+            FinalClassField.newInstance(EditorInfo.class, "IME_FLAG_FORCE_ASCII",
+                    Build.VERSION.SDK_INT >= /* ICE_CREAM_SANDWICH */14 ? 0x80000000 : 0);
+
+    private ArrayAdapter<String> mAutoCompleteAdapter;
+
+    /** Called when the activity is first created. */
+    @SuppressLint("SetJavaScriptEnabled")
+    @Override
+    public void onCreate(final Bundle savedInstanceState) {
+        getApplicationInfo().flags |= ApplicationInfo_FLAG_SUPPORTS_RTL.value;
+        prefs = PreferenceManager.getDefaultSharedPreferences(this);
+        loadTheme();
+        loadSoftInputMode();
+
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        final String[] countries = getResources().getStringArray(R.array.countries_array);
+        mAutoCompleteAdapter = new ArrayAdapter<>(
+                this, android.R.layout.simple_dropdown_item_1line, countries);
+
+        final boolean navigateMode = getNavigateMode();
+        final ViewGroup vg = (ViewGroup) findViewById(R.id.edit_text_list);
+        final int n = vg.getChildCount();
+        fields = new View[n];
+        for (int i = 0; i < n; i++) {
+            final View v = vg.getChildAt(i);
+            if (v instanceof EditText) {
+                final int id = v.getId();
+                final EditText e = (EditText) v;
+                int inputType = e.getInputType();
+                int imeOptions = e.getImeOptions();
+                if (id == R.id.text_auto_correct_previous) {
+                    imeOptions &= ~EditorInfo.IME_MASK_ACTION;
+                    imeOptions |= EditorInfo.IME_ACTION_PREVIOUS;
+                }
+                if (id == R.id.text_force_ascii_flag) {
+                    imeOptions |= EditorInfo_IME_FLAG_FORCE_ASCII.value;
+                }
+                if (id == R.id.text_null) {
+                    inputType = InputType.TYPE_NULL;
+                }
+                if (id == R.id.text_restarting) {
+                    EchoingTextWatcher.attachTo(e);
+                }
+                if (navigateMode && i > 0) {
+                    imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
+                }
+                if (navigateMode && i < n - 1) {
+                    imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
+                }
+
+                e.setInputType(inputType);
+                e.setImeOptions(imeOptions);
+                setupHintText(e);
+                if (navigateMode) {
+                    e.setOnEditorActionListener(this);
+                }
+            }
+            if (v instanceof AutoCompleteTextView) {
+                final AutoCompleteTextView e = (AutoCompleteTextView) v;
+                e.setAdapter(mAutoCompleteAdapter);
+                e.setThreshold(1);
+            }
+            if (v instanceof WebView) {
+                final WebView wv = (WebView) v;
+                wv.getSettings().setJavaScriptEnabled(true);
+                wv.addJavascriptInterface(new Object() {
+                    @JavascriptInterface
+                    public String name() {
+                        return getThemeName();
+                    }
+                }, "theme");
+                wv.loadUrl("file:///android_asset/webview.html");
+            }
+            fields[i] = v;
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(final Menu menu) {
+        super.onCreateOptionsMenu(menu);
+
+        menu.add(Menu.NONE, MENU_NAVIGATE_ON, 0, getString(R.string.menu_navigate_on));
+        menu.add(Menu.NONE, MENU_NAVIGATE_OFF, 1, getString(R.string.menu_navigate_off));
+        menu.add(Menu.NONE, MENU_SOFTINPUT_VISIBLE, 2, getString(R.string.menu_softinput_visible));
+        menu.add(Menu.NONE, MENU_SOFTINPUT_HIDDEN, 3, getString(R.string.menu_softinput_hidden));
+        menu.add(Menu.NONE, MENU_CHANGE_THEME, 4, R.string.menu_change_theme);
+        try {
+            final PackageInfo pinfo = getPackageManager().getPackageInfo(getPackageName(), 0);
+            menu.add(Menu.NONE, MENU_VERSION, 5,
+                    getString(R.string.menu_version, pinfo.versionName))
+                    .setEnabled(false);
+        } catch (NameNotFoundException e) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(final MenuItem item) {
+        final int itemId = item.getItemId();
+        if (itemId == MENU_CHANGE_THEME) {
+            final List<CharSequence> items = new ArrayList<>();
+            for (final ThemeItem theme : ThemeItem.THEME_LIST) {
+                items.add(theme.name);
+            }
+            final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setTitle(R.string.menu_change_theme);
+            builder.setCancelable(true);
+            builder.setNegativeButton(android.R.string.cancel, null);
+            builder.setItems(items.toArray(new CharSequence[items.size()]), this);
+            builder.show();
+        } else if (itemId == MENU_NAVIGATE_ON || itemId == MENU_NAVIGATE_OFF) {
+            saveNavigateMode(itemId == MENU_NAVIGATE_ON);
+            restartActivity();
+        } else if (itemId == MENU_SOFTINPUT_VISIBLE || itemId == MENU_SOFTINPUT_HIDDEN) {
+            saveSoftInputMode(itemId == MENU_SOFTINPUT_VISIBLE);
+            restartActivity();
+        }
+        return true;
+    }
+
+    @Override
+    public void onClick(final DialogInterface dialog, final int which) {
+        saveTheme(ThemeItem.THEME_LIST.get(which));
+        restartActivity();
+    }
+
+    private void restartActivity() {
+        final Intent intent = getIntent();
+        finish();
+        startActivity(intent);
+    }
+
+    private static void setupHintText(final EditText e) {
+        final int imeOptions = e.getImeOptions();
+        String hint = (e instanceof MultiLineShortMessageEditText) ? "*" : "";
+        hint += inputTypeToString(e.getInputType());
+        String text;
+        if (e.getImeActionLabel() != null) {
+            text = "actionLabel<" + e.getImeActionLabel() + ":" + e.getImeActionId() + ">";
+        } else {
+            text = actionName(imeOptions & EditorInfo.IME_MASK_ACTION);
+        }
+        text = appendFlagText(text,
+                (imeOptions & EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0, "flagNoExtractUi");
+        text = appendFlagText(text,
+                (imeOptions & EditorInfo.IME_FLAG_NO_FULLSCREEN) != 0, "flagNoFullscreen");
+        text = appendFlagText(text,
+                (imeOptions & EditorInfo_IME_FLAG_FORCE_ASCII.value) != 0, "flagForceAscii");
+        text = appendFlagText(text,
+                (imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0, ">");
+        text = appendFlagText(text,
+                (imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0, "<");
+        if (text.length() > 0)
+            hint += " " + text;
+        final String privateOptions = e.getPrivateImeOptions();
+        if (!TextUtils.isEmpty(privateOptions)) {
+            hint += " (";
+            String sep = "";
+            for (final String opt : privateOptions.trim().split(",")) {
+                final String[] elem = opt.trim().split("\\.");
+                hint += sep + elem[elem.length - 1];
+                sep = ",";
+            }
+            hint += ")";
+        }
+        if (DEBUG_INPUT_TEXT) {
+            Log.d(TAG, String.format("class=0x%08x variation=0x%08x flags=0x%08x hint=%s",
+                    e.getInputType() & InputType.TYPE_MASK_CLASS,
+                    e.getInputType() & InputType.TYPE_MASK_VARIATION,
+                    e.getInputType() & InputType.TYPE_MASK_FLAGS, hint));
+        }
+        if (e.getId() == R.id.text_restarting) {
+            hint += " restarting";
+        }
+        e.setHint(hint);
+    }
+
+    private void saveBooleanPreference(final String key, final boolean value) {
+        final SharedPreferences.Editor editor = prefs.edit();
+        editor.putBoolean(key, value);
+        editor.apply();
+    }
+
+    private void saveStringPreference(final String key, final String value) {
+        final SharedPreferences.Editor editor = prefs.edit();
+        editor.putString(key, value);
+        editor.apply();
+    }
+
+    private void saveNavigateMode(final boolean enabled) {
+        saveBooleanPreference(PREF_NAVIGATE, enabled);
+    }
+
+    private boolean getNavigateMode() {
+        return prefs.getBoolean(PREF_NAVIGATE, false);
+    }
+
+    private void saveSoftInputMode(final boolean visible) {
+        saveBooleanPreference(PREF_SOFTINPUT, visible);
+    }
+
+    private void loadSoftInputMode() {
+        final boolean visible = prefs.getBoolean(PREF_SOFTINPUT, false);
+        final Window w = getWindow();
+        w.setSoftInputMode(visible
+                ? WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE
+                : WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
+    }
+
+    private void saveTheme(final ThemeItem theme) {
+        saveStringPreference(PREF_THEME, theme.name);
+    }
+
+    String getThemeName() {
+        return prefs.getString(PREF_THEME, ThemeItem.getDefaultThemeName());
+    }
+
+    private void loadTheme() {
+        final String themeName = getThemeName();
+        for (final ThemeItem theme : ThemeItem.THEME_LIST) {
+            if (themeName.equals(theme.name)) {
+                setTheme(theme.id);
+                return;
+            }
+        }
+    }
+
+    @Override
+    public boolean onEditorAction(final TextView v, final int action, final KeyEvent event) {
+        for (int i = 0; i < fields.length; i++) {
+            if (v == fields[i]) {
+                final int direction;
+                if (action == EditorInfo.IME_ACTION_PREVIOUS) {
+                    direction = -1;
+                } else {
+                    direction = +1;
+                }
+
+                final int target = i + direction;
+                if (target < 0 || target >= fields.length)
+                    return false;
+
+                final View targetView = fields[target];
+                targetView.requestFocus();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static String actionName(final int action) {
+        switch (action & EditorInfo.IME_MASK_ACTION) {
+        case EditorInfo.IME_ACTION_UNSPECIFIED:
+            return "actionUnspecified";
+        case EditorInfo.IME_ACTION_NONE:
+            return "actionNone";
+        case EditorInfo.IME_ACTION_GO:
+            return "actionGo";
+        case EditorInfo.IME_ACTION_SEARCH:
+            return "actionSearch";
+        case EditorInfo.IME_ACTION_SEND:
+            return "actionSend";
+        case EditorInfo.IME_ACTION_NEXT:
+            return "actionNext";
+        case EditorInfo.IME_ACTION_DONE:
+            return "actionDone";
+        case EditorInfo.IME_ACTION_PREVIOUS:
+            return "actionPrevious";
+        default:
+            return "actionUnknown(" + action + ")";
+        }
+    }
+
+    private static String inputTypeToString(final int inputType) {
+        if (inputType == InputType.TYPE_NULL) {
+            return "TYPE_NULL";
+        }
+        final int clazz = inputType & InputType.TYPE_MASK_CLASS;
+        final int variation = inputType & InputType.TYPE_MASK_VARIATION;
+        final int flags = inputType & InputType.TYPE_MASK_FLAGS;
+        String base = "unknown(class=" + clazz + " variation=" + variation + " flag=0x"
+                + Integer.toHexString(flags);
+
+        switch (clazz) {
+        case InputType.TYPE_CLASS_TEXT:
+            switch (variation) {
+            case InputType.TYPE_TEXT_VARIATION_NORMAL:
+                base = "text";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_URI:
+                base = "textUri";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS:
+                base = "textEmailAddress";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT:
+                base = "textEmailSubject";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE:
+                base = "textShortMessage";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE:
+                base = "textLongMessage";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_PERSON_NAME:
+                base = "textPersonName";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS:
+                base = "textPostalAddress";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_PASSWORD:
+                base = "textPassword";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD:
+                base = "textVisiblePassword";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT:
+                base = "textWebEditText";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_FILTER:
+                base = "textFilter";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_PHONETIC:
+                base = "textPhonetic";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS:
+                base = "textWebEmailAddress";
+                break;
+            case InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD:
+                base = "textWebPassword";
+                break;
+            }
+            base = appendFlagText(base, (flags & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0,
+                    "textCapCharacters");
+            base = appendFlagText(base, (flags & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0,
+                    "textCapWords");
+            base = appendFlagText(base, (flags & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0,
+                    "textCapSentences");
+            base = appendFlagText(base, (flags & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) != 0,
+                    "textAutoCorrect");
+            base = appendFlagText(base, (flags & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0,
+                    "textAutoComplete");
+            base = appendFlagText(base, (flags & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0,
+                    "textMultiLine");
+            base = appendFlagText(base, (flags & InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE) != 0,
+                    "textImeMultiLine");
+            base = appendFlagText(base, (flags & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0,
+                    "textNoSuggestions");
+            break;
+
+        case InputType.TYPE_CLASS_NUMBER:
+            if (variation == InputType.TYPE_NUMBER_VARIATION_NORMAL) {
+                base = "number";
+            } else if (variation == InputType.TYPE_NUMBER_VARIATION_PASSWORD) {
+                base = "numberPassword";
+            }
+            base = appendFlagText(base, (flags & InputType.TYPE_NUMBER_FLAG_SIGNED) != 0,
+                    "numberSigned");
+            base = appendFlagText(base, (flags & InputType.TYPE_NUMBER_FLAG_DECIMAL) != 0,
+                    "numberDecimal");
+            break;
+
+        case InputType.TYPE_CLASS_PHONE:
+            base = "phone";
+            break;
+
+        case InputType.TYPE_CLASS_DATETIME:
+            switch (variation) {
+            case InputType.TYPE_DATETIME_VARIATION_NORMAL:
+                base = "datetime";
+                break;
+            case InputType.TYPE_DATETIME_VARIATION_DATE:
+                base = "date";
+                break;
+            case InputType.TYPE_DATETIME_VARIATION_TIME:
+                base = "time";
+                break;
+            }
+            break;
+        }
+
+        return base;
+    }
+
+    private static String appendFlagText(final String text, final boolean flag, final String name) {
+        if (flag) {
+            if (text.length() == 0 || name.startsWith(text))
+                return name;
+            return text + "|" + name;
+        }
+        return text;
+    }
+}
diff --git a/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/FinalClassField.java b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/FinalClassField.java
new file mode 100644
index 0000000..14c556a
--- /dev/null
+++ b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/FinalClassField.java
@@ -0,0 +1,64 @@
+/*
+ * 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.tools.edittextvariations;
+
+import java.lang.reflect.Field;
+
+public final class FinalClassField<T> {
+    public final boolean defined;
+    public final String name;
+    public final String className;
+    public final T value;
+
+    @SuppressWarnings("unchecked")
+    private FinalClassField(final Field field, final String className, final String fieldName,
+            final T compatValue) {
+        this.defined = field != null;
+        this.name = fieldName;
+        this.className = className;
+        T v = null;
+        try {
+            final Object obj = field.get(null);
+            v = (T) obj;
+        } catch (final Exception e) {
+            v = compatValue;
+        }
+        this.value = v;
+    }
+
+    public static <T> FinalClassField<T> newInstance(final Class<?> definedClass, final String name,
+            final T compatValue) {
+        if (definedClass == null)
+            throw new NullPointerException("defined class");
+        String className = definedClass.getCanonicalName();
+        try {
+            return new FinalClassField<>(
+                    definedClass.getField(name), className, name, compatValue);
+        } catch (Exception e) {
+            return new FinalClassField<>(null, className, name, compatValue);
+        }
+    }
+
+    public static <T> FinalClassField<T> newInstance(final String className, final String fieldName,
+            final T compatValue) {
+        try {
+            return newInstance(Class.forName(className), fieldName, compatValue);
+        } catch (ClassNotFoundException e) {
+            return new FinalClassField<>(null, className, fieldName, compatValue);
+        }
+    }
+}
diff --git a/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/InstanceMethod.java b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/InstanceMethod.java
new file mode 100644
index 0000000..05dc0d6
--- /dev/null
+++ b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/InstanceMethod.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2011 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.tools.edittextvariations;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public final class InstanceMethod {
+    public final boolean defined;
+    public final String name;
+    public final String className;
+
+    private final Class<?> clazz;
+    private final Method method;
+
+    private InstanceMethod(final Class<?> receiverClass, final Method instanceMethod,
+            final String receiverName, final String methodName) {
+        this.defined = instanceMethod != null;
+        this.clazz = receiverClass;
+        this.method = instanceMethod;
+        this.name = methodName;
+        this.className = receiverName;
+    }
+
+    public Object invoke(final Object receiverObject, final Object... args) {
+        if (!defined)
+            throw new RuntimeException("method " + name + " not defined");
+        if (receiverObject == null)
+            throw new NullPointerException("receiver object");
+        if (clazz.isInstance(receiverObject)) {
+            try {
+                if (args.length == 0) {
+                    return method.invoke(receiverObject);
+                }
+                return method.invoke(clazz, args);
+            } catch (IllegalArgumentException e) {
+                throw new RuntimeException("IllegalArgumentException");
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException("IllegalAccessException");
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException("InvocationTargetException");
+            }
+        }
+        throw new RuntimeException("receiver type not matched: method=" + name
+                + " actual receiver=" + receiverObject.getClass().getCanonicalName());
+    }
+
+    public static InstanceMethod newInstance(final Class<?> receiverClass, final String methodName,
+            final Class<?>... parameterTypes) {
+        if (receiverClass == null)
+            throw new NullPointerException("receiver class");
+        final String className = receiverClass.getCanonicalName();
+        try {
+            return new InstanceMethod(receiverClass,
+                    receiverClass.getMethod(methodName, parameterTypes), className, methodName);
+        } catch (Exception e) {
+            return new InstanceMethod(receiverClass, null, className, methodName);
+        }
+    }
+
+    public static InstanceMethod newInstance(final String className, final String methodName,
+            final Class<?>... parameterTypes) {
+        try {
+            return newInstance(Class.forName(className), methodName, parameterTypes);
+        } catch (ClassNotFoundException e) {
+            return new InstanceMethod(null, null, className, methodName);
+        }
+    }
+}
diff --git a/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/MultiLineShortMessageEditText.java b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/MultiLineShortMessageEditText.java
new file mode 100644
index 0000000..7ab7007
--- /dev/null
+++ b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/MultiLineShortMessageEditText.java
@@ -0,0 +1,46 @@
+/*
+ * 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.tools.edittextvariations;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.EditText;
+
+public final class MultiLineShortMessageEditText extends EditText {
+
+    public MultiLineShortMessageEditText(final Context context) {
+        super(context);
+    }
+
+    public MultiLineShortMessageEditText(final Context context, final AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public MultiLineShortMessageEditText(final Context context, final AttributeSet attrs,
+            final int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(final EditorInfo outAttrs) {
+        final InputConnection ic = super.onCreateInputConnection(outAttrs);
+        outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
+        return ic;
+    }
+}
diff --git a/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/ThemeItem.java b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/ThemeItem.java
new file mode 100644
index 0000000..f3c6d4f
--- /dev/null
+++ b/tools/EditTextVariations/src/com/android/inputmethod/tools/edittextvariations/ThemeItem.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 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.tools.edittextvariations;
+
+import android.os.Build;
+
+import com.android.inputmethod.tools.edittextvariations.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public final class ThemeItem {
+    public final int id;
+    public final String name;
+
+    private ThemeItem(final String name, final int resId) {
+        this.id = resId;
+        this.name = name;
+    }
+
+    private static final String THEME_DEFAULT = "Default";
+    private static final String THEME_HOLO = "Theme_Holo";
+    private static final String THEME_HOLO_LIGHT = "Theme_Holo_Light";
+    private static final String THEME_DEVICE_DEFAULT = "Theme_DeviceDefault";
+    private static final String THEME_DEVICE_DEFAULT_LIGHT = "Theme_DeviceDefault_Light";
+    private static final String THEME_MATERIAL = "Theme_Material";
+    private static final String THEME_MATERIAL_LIGHT = "Theme_Material_Light";
+
+    public static String getDefaultThemeName() {
+        return THEME_DEFAULT;
+    }
+
+    public static final List<ThemeItem> THEME_LIST = createThemeList(
+            THEME_HOLO, THEME_HOLO_LIGHT, THEME_DEVICE_DEFAULT, THEME_DEVICE_DEFAULT_LIGHT,
+            THEME_MATERIAL, THEME_MATERIAL_LIGHT);
+
+    private static List<ThemeItem> createThemeList(final String... candidateList) {
+        final ArrayList<ThemeItem> list = new ArrayList<>();
+
+        // Default theme is always available as it's defined in our resource.
+        list.add(new ThemeItem(THEME_DEFAULT, R.style.defaultActivityTheme));
+
+        for (final String name : candidateList) {
+            final FinalClassField<Integer> constant =
+                    FinalClassField.newInstance(android.R.style.class, name, 0);
+            if (constant.defined) {
+                list.add(new ThemeItem(name, constant.value));
+            }
+        }
+
+        return Collections.unmodifiableList(list);
+    }
+}