New ime jank test suite

Eight Tests in total:
1. Open handwriting canvas from remote input
2. Open handwriting canvas from input box activity
3. Open keyboard from remote input
4. Open keyboard from input box activity
5. keyboard simple input
6. keyboard complex input (including spaces and del)
7. keyboard gesture input
8. keyboard toggle more candidates.

Change-Id: I776488ffc88b19fc1a2f302245b011a27a068ff3
diff --git a/tests/jank/ime_wear/Android.mk b/tests/jank/ime_wear/Android.mk
new file mode 100644
index 0000000..e94e9b1
--- /dev/null
+++ b/tests/jank/ime_wear/Android.mk
@@ -0,0 +1,26 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# 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_PACKAGE_NAME := IMEJankTestsWear
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator ub-janktesthelper
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/tests/jank/ime_wear/AndroidManifest.xml b/tests/jank/ime_wear/AndroidManifest.xml
new file mode 100644
index 0000000..b60d7bc
--- /dev/null
+++ b/tests/jank/ime_wear/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.wearable.ime.janktests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <instrumentation
+            android:name="android.test.InstrumentationTestRunner"
+            android:targetPackage="com.android.wearable.ime.janktests"
+            android:label="Wearable IME Jank Tests" />
+</manifest>
\ No newline at end of file
diff --git a/tests/jank/ime_wear/src/com/android/wearable/ime/janktests/HandwritingJankTests.java b/tests/jank/ime_wear/src/com/android/wearable/ime/janktests/HandwritingJankTests.java
new file mode 100644
index 0000000..9b47464
--- /dev/null
+++ b/tests/jank/ime_wear/src/com/android/wearable/ime/janktests/HandwritingJankTests.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 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.wearable.ime.janktests;
+
+import android.os.Bundle;
+import android.support.test.jank.JankTest;
+import android.support.test.jank.JankTestBase;
+import android.support.test.jank.WindowAnimationFrameStatsMonitor;
+import android.support.test.uiautomator.UiDevice;
+
+/**
+ * Jank tests for handwriting on wear
+ */
+public class HandwritingJankTests extends JankTestBase {
+
+    private UiDevice mDevice;
+    private IMEJankTestsHelper mHelper;
+
+    /*
+     * (non-Javadoc)
+     * @see junit.framework.TestCase#setUp()
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mHelper = IMEJankTestsHelper.getInstance(mDevice, this.getInstrumentation());
+        mDevice.wakeUp();
+        mHelper.activateIMEHandwriting();
+    }
+
+    public void launchRemoteInputActivity() {
+        mHelper.goBackHome();
+        mHelper.launchRemoteInputActivity();
+    }
+
+    public void launchInputBoxActivity() {
+        mHelper.goBackHome();
+        mHelper.launchInputBoxActivity();
+    }
+
+    // Measure handwriting jank when open from remote input
+    @JankTest(beforeTest = "launchRemoteInputActivity",
+            afterLoop = "pressBack",
+            afterTest = "goBackHome",
+            expectedFrames = IMEJankTestsHelper.WFM_EXPECTED_FRAMES)
+    @WindowAnimationFrameStatsMonitor
+    public void testOpenHandwritingFromRemoteInput() {
+        mHelper.tapIMEButton();
+    }
+
+    // Measure handwriting jank when open from input box
+    @JankTest(beforeTest = "launchInputBoxActivity",
+            afterLoop = "pressBack",
+            afterTest = "goBackHome",
+            expectedFrames = IMEJankTestsHelper.WFM_EXPECTED_FRAMES)
+    @WindowAnimationFrameStatsMonitor
+    public void testOpenHandwritingFromInputBox() {
+        mHelper.tapOnScreen();
+    }
+
+    public void pressBack() {
+        mHelper.pressBack();
+    }
+
+    // Ensuring that we head back to the first screen before launching the app again
+    public void goBackHome(Bundle metrics) {
+        mHelper.goBackHome();
+        super.afterTest(metrics);
+    }
+
+}
\ No newline at end of file
diff --git a/tests/jank/ime_wear/src/com/android/wearable/ime/janktests/IMEJankTestsHelper.java b/tests/jank/ime_wear/src/com/android/wearable/ime/janktests/IMEJankTestsHelper.java
new file mode 100644
index 0000000..925c42c
--- /dev/null
+++ b/tests/jank/ime_wear/src/com/android/wearable/ime/janktests/IMEJankTestsHelper.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2016 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.wearable.ime.janktests;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.io.IOException;
+
+/**
+ * Helpers for Wear IME Jank Tests
+ */
+
+public class IMEJankTestsHelper {
+
+    public static final int LONG_TIMEOUT = 5000;
+    public static final int SHORT_TIMEOUT = 1500;
+    public static final int WFM_EXPECTED_FRAMES = 10;
+    public static final int GFX_EXPECTED_FRAMES = 10;
+    public static final String IME_PACKAGE_NAME = "com.google.android.inputmethod.latin";
+    private static final String LOG_TAG = IMEJankTestsHelper.class.getSimpleName();
+    private static final String HOME_INDICATOR = "charging_icon";
+    private static final String LAUNCHER_VIEW_NAME = "launcher_view";
+    private static final String CARD_TITLE_NAME = "title";
+    private static final String IME_BUTTON_NAME = "ime_choice";
+    private static final String REPLY_BUTTON_TEXT = "Reply";
+    private static final String REMOTE_INPUT_TEXT = "Quick reply";
+    private static final String REMOTE_INPUT_PACKAGE_NAME =
+            "com.google.android.googlequicksearchbox";
+    private static final String RELOAD_NOTIFICATION_CARD_INTENT = "com.google.android.wearable."
+            + "support.wearnotificationgenerator.SHOW_NOTIFICATION";
+    private static final String KEYBOARD_ID =
+            "com.google.android.inputmethod.latin/com.google.android.apps.inputmethod.wear.WearIME";
+    private static final String HANDWRITING_ID = "com.google.android.apps.handwriting.ime/"
+            + "com.google.android.wearable.input.handwriting.HandwriterInputMethodService";
+    private static final String SET_IME_CMD = "ime set %s";
+    private static final String ENABLE_IME_CMD = "ime enable %s";
+    private static final String INPUT_BOX_PACKAGE_NAME =
+            "com.google.android.wearable.input.latin.activity";
+    private static IMEJankTestsHelper mInstance;
+    private UiDevice mDevice;
+    private Instrumentation mInstrumentation;
+
+    private IMEJankTestsHelper(UiDevice device, Instrumentation instrumentation) {
+        mDevice = device;
+        mInstrumentation = instrumentation;
+    }
+
+    public static IMEJankTestsHelper getInstance(UiDevice device, Instrumentation instrumentation) {
+        if (mInstance == null) {
+            mInstance = new IMEJankTestsHelper(device, instrumentation);
+        }
+        return mInstance;
+    }
+
+    public void swipeUp() {
+        mDevice.swipe(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight() / 2 + 50,
+                mDevice.getDisplayWidth() / 2, 0, 30); // slow speed
+        SystemClock.sleep(SHORT_TIMEOUT);
+    }
+
+    public void swipeRight(int heightOffset) {
+        mDevice.swipe(50,
+                mDevice.getDisplayHeight() / 2 + heightOffset, mDevice.getDisplayWidth() - 25,
+                mDevice.getDisplayHeight() / 2 + heightOffset, 30); // slow speed
+        SystemClock.sleep(SHORT_TIMEOUT);
+    }
+
+    // Helper function to go back to home screen
+    public void goBackHome() {
+        String launcherPackage = mDevice.getLauncherPackageName();
+        UiObject2 homeScreen = mDevice.findObject(By.res(launcherPackage, HOME_INDICATOR));
+        int count = 0;
+        while (homeScreen == null && count < 5) {
+            mDevice.pressBack();
+            Log.d(LOG_TAG, "Pressed back");
+            homeScreen = mDevice.findObject(By.res(launcherPackage, HOME_INDICATOR));
+            count++;
+        }
+
+        // TODO (yuanlang@) Delete the following hacky codes after charging icon issue fixed
+        // App launcher still draw changing icon. Make sure we're not in the launcher
+        homeScreen = mDevice.findObject(By.res(launcherPackage, LAUNCHER_VIEW_NAME));
+        if (homeScreen != null) {
+            mDevice.pressBack();
+        }
+
+        SystemClock.sleep(SHORT_TIMEOUT);
+    }
+
+    public void launchRemoteInputActivity() {
+        String launcherPackage = mDevice.getLauncherPackageName();
+        // Swipe up 5 times to bring up demo cards
+        // in case there are some real cards on top
+        reloadDemoCards();
+        int i = 0;
+        UiObject2 cardTitle = null;
+        while (i < 5) {
+            swipeUp();
+            cardTitle = mDevice.wait(
+                    Until.findObject(By.text("Clockwork Notifications-10")), SHORT_TIMEOUT);
+            if (cardTitle != null) {
+                cardTitle.click();
+                break;
+            }
+            i ++;
+        }
+        Assert.assertNotNull("Cannot find demo card with remote input", cardTitle);
+        // Click on reply action button
+        UiObject2 replyButton = mDevice
+                .wait(Until.findObject(By.text(REPLY_BUTTON_TEXT)), LONG_TIMEOUT);
+        Assert.assertNotNull(replyButton);
+        replyButton.click();
+
+        // Make sure remote input activity launched
+        UiObject2 replyText = mDevice
+                .wait(Until.findObject(By.text(REMOTE_INPUT_TEXT)), LONG_TIMEOUT);
+        Assert.assertNotNull(replyText);
+    }
+
+    public void launchInputBoxActivity() {
+        Context context = mInstrumentation.getContext();
+        Intent intent = context.getPackageManager()
+                .getLaunchIntentForPackage(INPUT_BOX_PACKAGE_NAME);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        context.startActivity(intent);
+        Assert.assertTrue("Input box activity not started",
+                mDevice.wait(Until.hasObject(By.pkg(INPUT_BOX_PACKAGE_NAME).depth(0)),
+                        LONG_TIMEOUT));
+    }
+
+    public void tapIMEButton() {
+        UiObject2 imeButton = mDevice
+                .findObject(By.res(REMOTE_INPUT_PACKAGE_NAME, IME_BUTTON_NAME));
+        Assert.assertNotNull(imeButton);
+        imeButton.click();
+        SystemClock.sleep(SHORT_TIMEOUT);
+    }
+
+    // This will ensure to reload notification cards by launching NotificationsGeneratorWear app
+    // when there are insufficient cards.
+    private void reloadDemoCards() {
+        Intent intent = new Intent();
+        intent.setAction(RELOAD_NOTIFICATION_CARD_INTENT);
+        mInstrumentation.getContext().sendBroadcast(intent);
+        Log.d(LOG_TAG, "Demo cards have been reloaded");
+        SystemClock.sleep(LONG_TIMEOUT);
+    }
+
+    public void activateIMEKeyboard() {
+        try {
+            mDevice.executeShellCommand(String.format(ENABLE_IME_CMD, KEYBOARD_ID));
+            mDevice.executeShellCommand(String.format(SET_IME_CMD, KEYBOARD_ID));
+        } catch (IOException e) {
+            Log.e(LOG_TAG, e.toString());
+        }
+        Log.d(LOG_TAG, "Keyboard activated");
+    }
+
+    public void activateIMEHandwriting() {
+        try {
+            mDevice.executeShellCommand(String.format(ENABLE_IME_CMD, HANDWRITING_ID));
+            mDevice.executeShellCommand(String.format(SET_IME_CMD, HANDWRITING_ID));
+        } catch (IOException e) {
+            Log.e(LOG_TAG, e.toString());
+        }
+        Log.d(LOG_TAG, "Handwriting activated");
+    }
+
+    public void pressBack() {
+        mDevice.pressBack();
+        SystemClock.sleep(SHORT_TIMEOUT);
+    }
+
+    public void tapOnScreen() {
+        mDevice.click(mDevice.getDisplayHeight() / 2, mDevice.getDisplayWidth() / 2);
+        SystemClock.sleep(SHORT_TIMEOUT);
+    }
+
+    public void clickSoftKey(String softKeyDescription) {
+        UiObject2 softKey = mDevice.wait(
+                Until.findObject(By.res(IME_PACKAGE_NAME, keyPosIdByDesc(softKeyDescription))),
+                SHORT_TIMEOUT);
+        Assert.assertNotNull("Soft Key " + softKeyDescription + " not found in UI", softKey);
+        softKey.click();
+    }
+
+    private String keyPosIdByDesc(String desc) {
+        return String.format("key_pos_%s", desc);
+    }
+}
\ No newline at end of file
diff --git a/tests/jank/ime_wear/src/com/android/wearable/ime/janktests/KeyboardJankTests.java b/tests/jank/ime_wear/src/com/android/wearable/ime/janktests/KeyboardJankTests.java
new file mode 100644
index 0000000..b6f7661
--- /dev/null
+++ b/tests/jank/ime_wear/src/com/android/wearable/ime/janktests/KeyboardJankTests.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 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.wearable.ime.janktests;
+
+import android.os.Bundle;
+import android.support.test.jank.GfxMonitor;
+import android.support.test.jank.JankTest;
+import android.support.test.jank.JankTestBase;
+import android.support.test.jank.WindowAnimationFrameStatsMonitor;
+import android.support.test.uiautomator.UiDevice;
+
+/**
+ * Jank tests for keyboard on wear
+ */
+public class KeyboardJankTests extends JankTestBase {
+
+    private UiDevice mDevice;
+    private IMEJankTestsHelper mHelper;
+
+    /*
+     * (non-Javadoc)
+     * @see junit.framework.TestCase#setUp()
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mHelper = IMEJankTestsHelper.getInstance(mDevice, this.getInstrumentation());
+        mDevice.wakeUp();
+        mHelper.activateIMEKeyboard();
+    }
+
+    public void launchRemoteInputActivity() {
+        mHelper.goBackHome();
+        mHelper.launchRemoteInputActivity();
+    }
+
+    public void launchInputBoxActivity() {
+        mHelper.goBackHome();
+        mHelper.launchInputBoxActivity();
+    }
+
+    // Measure keyboard jank when opened from remote input
+    @JankTest(beforeTest = "launchRemoteInputActivity",
+            afterLoop = "pressBack",
+            afterTest = "goBackHome",
+            expectedFrames = IMEJankTestsHelper.WFM_EXPECTED_FRAMES)
+    @WindowAnimationFrameStatsMonitor
+    public void testOpenKeyboardFromRemoteInput() {
+        mHelper.tapIMEButton();
+    }
+
+    // Measure keyboard jank when opened from input box
+    @JankTest(beforeTest = "launchInputBoxActivity",
+            afterLoop = "pressBack",
+            afterTest = "goBackHome",
+            expectedFrames = IMEJankTestsHelper.WFM_EXPECTED_FRAMES)
+    @WindowAnimationFrameStatsMonitor
+    public void testOpenKeyboardFromInputBox() {
+        mHelper.tapOnScreen();
+    }
+
+    // Measure keyboard jank when typing simple sequence of characters
+    @JankTest(beforeLoop = "launchInputBoxActivity",
+            afterLoop = "pressBack",
+            afterTest = "goBackHome",
+            expectedFrames = IMEJankTestsHelper.GFX_EXPECTED_FRAMES)
+    @GfxMonitor(processName = IMEJankTestsHelper.IME_PACKAGE_NAME)
+    public void testKeyboardSimpleSequence() {
+        mHelper.tapOnScreen();
+        for (int i = 1; i <= 9; i++) {
+            mHelper.clickSoftKey(String.format("1_%d", i));
+        }
+    }
+
+    // Measure keyboard jank when typing complex sequence of characters, del and spaces
+    @JankTest(beforeLoop = "launchInputBoxActivity",
+            afterLoop = "pressBack",
+            afterTest = "goBackHome",
+            expectedFrames = IMEJankTestsHelper.GFX_EXPECTED_FRAMES)
+    @GfxMonitor(processName = IMEJankTestsHelper.IME_PACKAGE_NAME)
+    public void testKeyboardComplexSequence() {
+        mHelper.tapOnScreen();
+        for (int i = 0; i < 50; i++) {
+            mHelper.clickSoftKey(String.format("0_%d", i % 10));
+            if (i % 7 == 0) {
+                mHelper.clickSoftKey("space");
+            }
+            if (i % 15 == 7) {
+                mHelper.clickSoftKey("del");
+                mHelper.clickSoftKey("del");
+                mHelper.clickSoftKey("del");
+            }
+        }
+    }
+
+    // Measure keyboard jank when tapping more candidates button
+    @JankTest(beforeTest = "prepareToShowMoreCandidates",
+            afterTest = "goBackHome",
+            expectedFrames = IMEJankTestsHelper.GFX_EXPECTED_FRAMES)
+    @GfxMonitor(processName = IMEJankTestsHelper.IME_PACKAGE_NAME)
+    public void testKeyboardToggleMoreCandidates() {
+        for (int i = 0; i < 6; i++) {
+            mHelper.clickSoftKey("show_more_candidates");
+        }
+    }
+
+    // Measure keyboard jank when typing gesture sequence
+    @JankTest(beforeLoop = "launchInputBoxActivity",
+            afterLoop = "pressBack",
+            afterTest = "goBackHome",
+            expectedFrames = IMEJankTestsHelper.GFX_EXPECTED_FRAMES)
+    @GfxMonitor(processName = IMEJankTestsHelper.IME_PACKAGE_NAME)
+    public void testKeyboardGestureInput() {
+        mHelper.tapOnScreen();
+        for (int i = 0; i < 2; i++) {
+            // Swipe through first row on keyboard
+            mHelper.swipeRight(0);
+            // Swipe through second row on keyboard
+            mHelper.swipeRight(mDevice.getDisplayHeight() / 8);
+            // Swipe through third row on keyboard
+            mHelper.swipeRight(mDevice.getDisplayHeight() / 4);
+        }
+    }
+
+    // Make sure more candidates icon shows up
+    public void prepareToShowMoreCandidates() {
+        launchInputBoxActivity();
+        mHelper.tapOnScreen();
+        for (int i = 0; i < 5; i++) {
+            mHelper.clickSoftKey(String.format("0_%d", i));
+        }
+    }
+
+    public void pressBack() {
+        mHelper.pressBack();
+    }
+
+    // Ensuring that we head back to the first screen before launching the app again
+    public void goBackHome(Bundle metrics) {
+        mHelper.goBackHome();
+        super.afterTest(metrics);
+    }
+
+}
\ No newline at end of file