upload android base code part4
This commit is contained in:
parent
b9e30e05b1
commit
78ea2404cd
23455 changed files with 5250148 additions and 0 deletions
15
android/cts/hostsidetests/inputmethodservice/Android.mk
Normal file
15
android/cts/hostsidetests/inputmethodservice/Android.mk
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Copyright (C) 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.
|
||||
|
||||
include $(call all-subdir-makefiles)
|
|
@ -0,0 +1,50 @@
|
|||
# Copyright (C) 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.
|
||||
|
||||
###############################################################################
|
||||
# Build the common library for use device-side
|
||||
###############################################################################
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_JAVA_LIBRARIES := junit
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
# tag this module as a cts test artifact
|
||||
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
|
||||
|
||||
LOCAL_MODULE := CtsInputMethodServiceCommon
|
||||
|
||||
LOCAL_SDK_VERSION := current
|
||||
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
||||
###############################################################################
|
||||
# Build the common library for use host-side
|
||||
###############################################################################
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_JAVA_LIBRARIES := junit-host
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_MODULE := cts-inputmethodservice-common-host
|
||||
|
||||
include $(BUILD_HOST_JAVA_LIBRARY)
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.common;
|
||||
|
||||
/**
|
||||
* Utility class to build Android's component name.
|
||||
*/
|
||||
final class ComponentNameUtils {
|
||||
|
||||
// This is utility class, can't instantiate.
|
||||
private ComponentNameUtils() {}
|
||||
|
||||
/**
|
||||
* Build Android component name from {@code packageName} and {@code className}.
|
||||
* @param packageName package name of a component.
|
||||
* @param className class name of a component.
|
||||
* @return a component of {@code packageName/className} that can be used to specify component,
|
||||
* for example, for {@code android.content.Intent}.
|
||||
*/
|
||||
static String buildComponentName(final String packageName, final String className) {
|
||||
return packageName + "/" + (className.startsWith(packageName)
|
||||
? className.substring(packageName.length()) : className);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.common;
|
||||
|
||||
import android.inputmethodservice.cts.common.test.TestInfo;
|
||||
|
||||
/**
|
||||
* Constants of device event.
|
||||
*/
|
||||
public final class DeviceEventConstants {
|
||||
|
||||
// This is constants holding class, can't instantiate.
|
||||
private DeviceEventConstants() {}
|
||||
|
||||
/** Intent action in order to record IME events. */
|
||||
public static final String ACTION_DEVICE_EVENT =
|
||||
"android.inputmethodservice.cts.action.DEVICE_EVENT";
|
||||
|
||||
/**
|
||||
* Intent receiver's package, class, and component name.
|
||||
*/
|
||||
public static final String RECEIVER_PACKAGE = "android.inputmethodservice.cts.provider";
|
||||
public static final String RECEIVER_CLASS =
|
||||
"android.inputmethodservice.cts.receiver.EventReceiver";
|
||||
public static final String RECEIVER_COMPONENT = ComponentNameUtils.buildComponentName(
|
||||
RECEIVER_PACKAGE, RECEIVER_CLASS);
|
||||
|
||||
/**
|
||||
* Intent extra key for who sends a device event.
|
||||
* Values are Input Method class name, for example {@link Ime1Constants#CLASS}, or device test
|
||||
* method name, for example {@link TestInfo#getTestName()}).
|
||||
*
|
||||
* @see android.content.Intent#putExtra(String,String)
|
||||
* @see android.content.Intent#getStringExtra(String)
|
||||
*/
|
||||
public static final String EXTRA_EVENT_SENDER = "event_sender";
|
||||
|
||||
/**
|
||||
* Intent extra key for Event parameters like
|
||||
* {@link DeviceEventTypeParam#ON_START_INPUT_RESTARTING}
|
||||
*/
|
||||
public static final String EXTRA_EVENT_PARAMS = "event_params";
|
||||
|
||||
/**
|
||||
* Intent extra key for what type a device event is. Values are {@link DeviceEventType#name()}.
|
||||
*
|
||||
* @see android.content.Intent#putExtra(String,String)
|
||||
* @see android.content.Intent#getStringExtra(String)
|
||||
*/
|
||||
public static final String EXTRA_EVENT_TYPE = "event_type";
|
||||
|
||||
/**
|
||||
* Intent extra key for at what time a device event happens. Value is taken from
|
||||
* {@code android.os.SystemClock.uptimeMillis()}.
|
||||
*
|
||||
* @see android.content.Intent#putExtra(String,long)
|
||||
* @see android.content.Intent#getLongExtra(String,long)
|
||||
*/
|
||||
public static final String EXTRA_EVENT_TIME = "event_time";
|
||||
|
||||
/**
|
||||
* Parameter for {@link DeviceEventType}.
|
||||
*/
|
||||
public enum DeviceEventTypeParam {
|
||||
|
||||
/**
|
||||
* Param for {@link DeviceEventType#ON_START_INPUT}. Represents if IME is restarting.
|
||||
*/
|
||||
ON_START_INPUT_RESTARTING(DeviceEventType.ON_START_INPUT, "onStartInput.restarting");
|
||||
|
||||
private final DeviceEventType mType;
|
||||
private final String mName;
|
||||
|
||||
DeviceEventTypeParam(DeviceEventType type, String name) {
|
||||
mType = type;
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Types of device event, a value of {@link #EXTRA_EVENT_TYPE}.
|
||||
*/
|
||||
public enum DeviceEventType {
|
||||
/**
|
||||
* {@link android.inputmethodservice.InputMethodService#onCreate() onCreate()} callback.
|
||||
*/
|
||||
ON_CREATE,
|
||||
|
||||
/**
|
||||
* {@link android.inputmethodservice.InputMethodService#onStartInput(android.view.inputmethod.EditorInfo,boolean) onStartInput(EditorInfo,boolean}
|
||||
* callback.
|
||||
*/
|
||||
ON_START_INPUT,
|
||||
|
||||
/**
|
||||
* {@link android.inputmethodservice.InputMethodService#onStartInputView(android.view.inputmethod.EditorInfo, boolean) onStartInputView(EditorInfo,boolean}
|
||||
*/
|
||||
ON_START_INPUT_VIEW,
|
||||
|
||||
/**
|
||||
* {@link android.inputmethodservice.InputMethodService#onFinishInputView(boolean) onFinishInputView(boolean)}
|
||||
* callback.
|
||||
*/
|
||||
ON_FINISH_INPUT_VIEW,
|
||||
|
||||
/**
|
||||
* {@link android.inputmethodservice.InputMethodService#onFinishInput() onFinishInput()}
|
||||
* callback.
|
||||
*/
|
||||
ON_FINISH_INPUT,
|
||||
|
||||
/**
|
||||
* {@link android.inputmethodservice.InputMethodService#onDestroy() onDestroy()} callback.
|
||||
*/
|
||||
ON_DESTROY,
|
||||
|
||||
/** Test start and end event types. */
|
||||
TEST_START,
|
||||
TEST_END,
|
||||
|
||||
/**
|
||||
* {@link android.view.inputmethod.InputMethod#showSoftInput}
|
||||
*/
|
||||
SHOW_SOFT_INPUT,
|
||||
|
||||
/**
|
||||
* {@link android.view.inputmethod.InputMethod#hideSoftInput}
|
||||
*/
|
||||
HIDE_SOFT_INPUT,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.common;
|
||||
|
||||
/**
|
||||
* Constants of CtsInputMethodServiceEventProvider.apk that keeps IME event records.
|
||||
*/
|
||||
public final class EventProviderConstants {
|
||||
|
||||
// This is constants holding class, can't instantiate.
|
||||
private EventProviderConstants() {}
|
||||
|
||||
/** Package name of the IME event provider. */
|
||||
public static final String PACKAGE = "android.inputmethodservice.cts.provider";
|
||||
|
||||
/** Class name of IME event provider. */
|
||||
public static final String CLASS =
|
||||
"android.inputmethodservice.cts.provider.EventProviderConstants";
|
||||
|
||||
/** APK file name. */
|
||||
public static final String APK = "CtsInputMethodServiceEventProvider.apk";
|
||||
|
||||
/** The authority of IME event provider. */
|
||||
public static final String AUTHORITY = "android.inputmethodservice.cts.provider";
|
||||
|
||||
/** The base URI of IME event provider. */
|
||||
private static final String BASE_URI = "content://" + AUTHORITY;
|
||||
|
||||
/**
|
||||
* The Android platform's base MIME type for a content: URI containing a Cursor of a single
|
||||
* item. Copied from android.content.ContentResolver.CURSOR_ITEM_BASE_TYPE.
|
||||
*/
|
||||
private static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
|
||||
|
||||
/**
|
||||
* The Android platform's base MIME type for a content: URI containing a Cursor of zero or more
|
||||
* items. Copied from android.content.ContentResolver.CURSOR_DIR_BASE_TYPE.
|
||||
*/
|
||||
private static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
|
||||
|
||||
/** Constants of Events table of IME event provider. */
|
||||
public static final class EventTableConstants {
|
||||
|
||||
// This is constants holding class, can't instantiate.
|
||||
private EventTableConstants() {}
|
||||
|
||||
/** Column name of the table that holds Event extras in json format. */
|
||||
public static final String EXTRAS = "extras";
|
||||
|
||||
/** Name of the table in content provider and database. */
|
||||
public static final String NAME = "events";
|
||||
|
||||
/** Column name of the table that holds who sends an event. */
|
||||
public static final String SENDER = "sender";
|
||||
|
||||
/** Column name of the table that holds what type of event is. */
|
||||
public static final String TYPE = "type";
|
||||
|
||||
/** Column name of the table that holds when an event happens. */
|
||||
public static final String TIME = "time";
|
||||
|
||||
/** Content URI of the table. */
|
||||
public static final String CONTENT_URI = BASE_URI + "/" + NAME;
|
||||
|
||||
/** MIME type of a cursor of zero or more items of the table. */
|
||||
public static final String TYPE_DIR =
|
||||
CURSOR_DIR_BASE_TYPE + "/" + AUTHORITY + ".ime_event";
|
||||
|
||||
/** MIME tye of a cursor of a single item of the table. */
|
||||
public static final String TYPE_ITEM =
|
||||
CURSOR_ITEM_BASE_TYPE + "/" + AUTHORITY + ".ime_event";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.common;
|
||||
|
||||
public final class Ime1Constants {
|
||||
|
||||
// This is constants holding class, can't instantiate.
|
||||
private Ime1Constants() {}
|
||||
|
||||
public static final String PACKAGE = "android.inputmethodservice.cts.ime1";
|
||||
public static final String CLASS = "android.inputmethodservice.cts.ime1.CtsInputMethod1";
|
||||
public static final String APK = "CtsInputMethod1.apk";
|
||||
|
||||
public static final String IME_ID = ComponentNameUtils.buildComponentName(PACKAGE, CLASS);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.common;
|
||||
|
||||
public final class Ime2Constants {
|
||||
|
||||
// This is constants holding class, can't instantiate.
|
||||
private Ime2Constants() {}
|
||||
|
||||
public static final String PACKAGE = "android.inputmethodservice.cts.ime2";
|
||||
public static final String CLASS = "android.inputmethodservice.cts.ime2.CtsInputMethod2";
|
||||
public static final String APK = "CtsInputMethod2.apk";
|
||||
|
||||
public static final String IME_ID = ComponentNameUtils.buildComponentName(PACKAGE, CLASS);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.common;
|
||||
|
||||
/**
|
||||
* Constants of IME command android.content.Intent.
|
||||
*/
|
||||
public final class ImeCommandConstants {
|
||||
|
||||
// This is constants holding class, can't instantiate.
|
||||
private ImeCommandConstants() {}
|
||||
|
||||
/** Intent action in order to record IME events. */
|
||||
public static final String ACTION_IME_COMMAND =
|
||||
"android.inputmethodservice.cts.action.IME_COMMAND";
|
||||
|
||||
public static final String EXTRA_COMMAND = "command";
|
||||
|
||||
public static final String EXTRA_ARG_CHARSEQUENCE1 = "arg_charsequence1";
|
||||
public static final String EXTRA_ARG_STRING1 = "arg_string1";
|
||||
public static final String EXTRA_ARG_INT1 = "arg_int1";
|
||||
|
||||
/**
|
||||
* This command has the mock IME call {@link android.view.inputmethod.InputConnection#commitText(CharSequence,int) InputConnection#commitText(CharSequence text, int newCursorPosition)}.
|
||||
* <ul>
|
||||
* <li>argument {@code text} needs to be specified by {@link #EXTRA_ARG_CHARSEQUENCE1}.</li>
|
||||
* <li>argument {@code newCursorPosition} needs to be specified by {@link #EXTRA_ARG_INT1}.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final String COMMAND_COMMIT_TEXT = "commitText";
|
||||
|
||||
/**
|
||||
* This command has the mock IME call {@link android.inputmethodservice.InputMethodService#switchInputMethod(String)} InputMethodService#switchInputMethod(String imeId)}.
|
||||
* <ul>
|
||||
* <li>argument {@code imeId} needs to be specified by {@link #EXTRA_ARG_STRING1}.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final String COMMAND_SWITCH_INPUT_METHOD = "switchInputMethod";
|
||||
|
||||
/**
|
||||
* This command has the mock IME call {@link android.inputmethodservice.InputMethodService#requestHideSelf(int)} InputMethodService#requestHideSelf(int flags)}.
|
||||
* <ul>
|
||||
* <li>argument {@code flags} needs to be specified by {@link #EXTRA_ARG_INT1}.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public static final String COMMAND_REQUEST_HIDE_SELF = "requestHideSelf";
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.common.test;
|
||||
|
||||
/**
|
||||
* Constants of CtsInputMethodServiceDeviceTests.apk that contains tests on device side and
|
||||
* related activities for test.
|
||||
*/
|
||||
public final class DeviceTestConstants {
|
||||
|
||||
// This is constants holding class, can't instantiate.
|
||||
private DeviceTestConstants() {}
|
||||
|
||||
/** Package name of the APK. */
|
||||
public static final String PACKAGE = "android.inputmethodservice.cts.devicetest";
|
||||
|
||||
/** APK file name. */
|
||||
public static final String APK = "CtsInputMethodServiceDeviceTests.apk";
|
||||
|
||||
/** Device test activity name. */
|
||||
public static final String TEST_ACTIVITY_CLASS =
|
||||
"android.inputmethodservice.cts.devicetest.InputMethodServiceTestActivity";
|
||||
|
||||
/**
|
||||
* Device test class name and methods name.
|
||||
*/
|
||||
public static final String TEST_CLASS =
|
||||
"android.inputmethodservice.cts.devicetest.InputMethodServiceDeviceTest";
|
||||
public static final String TEST_CREATE_IME1 = "testCreateIme1";
|
||||
public static final String TEST_SWITCH_IME1_TO_IME2 = "testSwitchIme1ToIme2";
|
||||
public static final String TEST_IME1_IS_NOT_CURRENT_IME = "testIme1IsNotCurrentIme";
|
||||
public static final String TEST_SEARCH_VIEW_GIVE_FOCUS_SHOW_IME1
|
||||
= "testSearchView_giveFocusShowIme1";
|
||||
public static final String TEST_SEARCH_VIEW_SET_QUERY_HIDE_IME1
|
||||
= "testSearchView_setQueryHideIme1";
|
||||
public static final String TEST_ON_START_INPUT_CALLED_ONCE_IME1
|
||||
= "testOnStartInputCalledOnceIme1";
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.common.test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Utility class for preparing "adb shell" command.
|
||||
*/
|
||||
public final class ShellCommandUtils {
|
||||
|
||||
// This is utility class, can't instantiate.
|
||||
private ShellCommandUtils() {}
|
||||
|
||||
// Copied from android.content.pm.PackageManager#FEATURE_INPUT_METHODS.
|
||||
public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
|
||||
|
||||
/** Command to check whether system has specified {@code featureName} feature. */
|
||||
public static String hasFeature(final String featureName) {
|
||||
return "cmd package has-feature " + featureName;
|
||||
}
|
||||
|
||||
private static final String SETTING_DEFAULT_IME = "secure default_input_method";
|
||||
|
||||
/** Command to get ID of current IME. */
|
||||
public static String getCurrentIme() {
|
||||
return "settings get " + SETTING_DEFAULT_IME;
|
||||
}
|
||||
|
||||
/** Command to set current IME to {@code imeId}. */
|
||||
public static String setCurrentIme(final String imeId) {
|
||||
return "settings put " + SETTING_DEFAULT_IME + " " + imeId;
|
||||
}
|
||||
|
||||
/** Command to enable IME of {@code imeId}. */
|
||||
public static String enableIme(final String imeId) {
|
||||
return "ime enable " + imeId;
|
||||
}
|
||||
|
||||
/** Command to disable IME of {@code imeId}. */
|
||||
public static String disableIme(final String imeId) {
|
||||
return "ime disable " + imeId;
|
||||
}
|
||||
|
||||
/** Command to delete all records of IME event provider. */
|
||||
public static String deleteContent(final String contentUri) {
|
||||
return "content delete --uri " + contentUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Command to send broadcast {@code Intent}.
|
||||
*
|
||||
* @param action action of intent.
|
||||
* @param targetComponent target of intent.
|
||||
* @param extras extra of intent, must be specified as triplet of option flag, key, and value.
|
||||
* @return shell command to send broadcast intent.
|
||||
*/
|
||||
public static String broadcastIntent(final String action, final String targetComponent,
|
||||
final String... extras) {
|
||||
if (extras.length % 3 != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"extras must be triplets: " + Arrays.toString(extras));
|
||||
}
|
||||
final StringBuilder sb = new StringBuilder("am broadcast -a ")
|
||||
.append(action);
|
||||
for (int index = 0; index < extras.length; index += 3) {
|
||||
final String optionFlag = extras[index];
|
||||
final String extraKey = extras[index + 1];
|
||||
final String extraValue = extras[index + 2];
|
||||
sb.append(" ").append(optionFlag)
|
||||
.append(" ").append(extraKey)
|
||||
.append(" ").append(extraValue);
|
||||
}
|
||||
return sb.append(" ").append(targetComponent).toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.common.test;
|
||||
|
||||
import android.inputmethodservice.cts.common.DeviceEventConstants;
|
||||
|
||||
/**
|
||||
* Abstraction of test information on device.
|
||||
*/
|
||||
public final class TestInfo {
|
||||
|
||||
public final String testPackage;
|
||||
public final String testClass;
|
||||
public final String testMethod;
|
||||
|
||||
public TestInfo(final String testPackage, final String testClass, final String testMethod) {
|
||||
this.testPackage = testPackage;
|
||||
this.testClass = testClass;
|
||||
this.testMethod = testMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fully qualified test method name that can be used as
|
||||
* {@link DeviceEventConstants#EXTRA_EVENT_SENDER}.
|
||||
* @return string representation of fully qualified test method name.
|
||||
*/
|
||||
public String getTestName() {
|
||||
return testClass + "#" + testMethod;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
# Copyright (C) 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.
|
||||
|
||||
include $(call all-subdir-makefiles)
|
|
@ -0,0 +1,45 @@
|
|||
# Copyright (C) 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.
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# Don't include this package in any target
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
# When built, explicitly put it in the data partition.
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
|
||||
|
||||
LOCAL_DEX_PREOPT := false
|
||||
|
||||
LOCAL_PROGUARD_ENABLED := disabled
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_JAVA_RESOURCE_DIR := res
|
||||
LOCAL_JAVA_LIBRARY := android.test.runner
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
android-support-test \
|
||||
hamcrest hamcrest-library \
|
||||
ub-uiautomator \
|
||||
CtsInputMethodServiceCommon \
|
||||
CtsInputMethodServiceLib
|
||||
|
||||
# tag this module as a cts test artifact
|
||||
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
|
||||
|
||||
LOCAL_PACKAGE_NAME := CtsInputMethodServiceDeviceTests
|
||||
|
||||
LOCAL_SDK_VERSION := current
|
||||
|
||||
include $(BUILD_CTS_PACKAGE)
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="android.inputmethodservice.cts.devicetest">
|
||||
|
||||
<!--
|
||||
TODO: We may need to have another new APK that has the latest targetSdkVersion to check the
|
||||
latest OS behaviors.
|
||||
-->
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25" />
|
||||
|
||||
<application
|
||||
android:label="CtsInputMethodServiceDeviceTests"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:allowBackup="false"
|
||||
>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
|
||||
<activity
|
||||
android:name=".InputMethodServiceTestActivity"
|
||||
android:label="InputMethodSercuceTestActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:name="android.support.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="android.inputmethodservice.cts.devicetest" />
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="10px">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/text_entry"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:drawable/editbox_background"/>
|
||||
<SearchView
|
||||
android:id="@+id/search_view"
|
||||
android:layout_below="@id/text_entry"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:iconifiedByDefault="false"
|
||||
android:queryHint="hint"
|
||||
android:inputType="textCapCharacters"
|
||||
android:imeOptions="actionDone" />
|
||||
|
||||
</RelativeLayout>
|
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.devicetest;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Utility class for busy waiting.
|
||||
*/
|
||||
final class BusyWaitUtils {
|
||||
|
||||
private static final long POLLING_INTERVAL = TimeUnit.MILLISECONDS.toMillis(50);
|
||||
|
||||
@FunctionalInterface
|
||||
interface PollingCondition {
|
||||
boolean check() throws Exception;
|
||||
}
|
||||
|
||||
// This is utility class, can't instantiate.
|
||||
private BusyWaitUtils() {}
|
||||
|
||||
/**
|
||||
* Busy waiting until {@link PollingCondition#check()} returns {@code true}.
|
||||
* @param condition {@link PollingCondition} to be checked.
|
||||
* @param timeout milliseconds before time out happens.
|
||||
* @param message when time out happens, {@link org.junit.Assert#fail(String)} is called with
|
||||
* this message.
|
||||
* @throws Exception
|
||||
*/
|
||||
static void pollingCheck(final PollingCondition condition, final long timeout,
|
||||
final String message) throws Exception {
|
||||
if (waitFor(condition, timeout)) {
|
||||
return;
|
||||
}
|
||||
fail(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Busy waiting until {@link PollingCondition#check()} returns {@code true}.
|
||||
* @param condition {@link PollingCondition} to be checked.
|
||||
* @param timeout milliseconds before time out happens.
|
||||
* @return true when {@code condition} returns {@code true}, false when timed out.
|
||||
* @throws Exception
|
||||
*/
|
||||
static boolean waitFor(final PollingCondition condition, final long timeout) throws Exception {
|
||||
for (long remaining = timeout; remaining > 0; remaining -= POLLING_INTERVAL) {
|
||||
if (condition.check()) {
|
||||
return true;
|
||||
}
|
||||
Thread.sleep(POLLING_INTERVAL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.devicetest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import static android.inputmethodservice.cts.DeviceEvent.isFrom;
|
||||
import static android.inputmethodservice.cts.DeviceEvent.isNewerThan;
|
||||
import static android.inputmethodservice.cts.DeviceEvent.isType;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.HIDE_SOFT_INPUT;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_CREATE;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_DESTROY;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_FINISH_INPUT;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_START_INPUT;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.SHOW_SOFT_INPUT;
|
||||
import static android.inputmethodservice.cts.common.ImeCommandConstants.ACTION_IME_COMMAND;
|
||||
import static android.inputmethodservice.cts.common.ImeCommandConstants.COMMAND_SWITCH_INPUT_METHOD;
|
||||
import static android.inputmethodservice.cts.common.ImeCommandConstants.EXTRA_ARG_STRING1;
|
||||
import static android.inputmethodservice.cts.common.ImeCommandConstants.EXTRA_COMMAND;
|
||||
import static android.inputmethodservice.cts.devicetest.BusyWaitUtils.pollingCheck;
|
||||
import static android.inputmethodservice.cts.devicetest.MoreCollectors.startingFrom;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.inputmethodservice.cts.DeviceEvent;
|
||||
import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType;
|
||||
import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventTypeParam;
|
||||
import android.inputmethodservice.cts.common.Ime1Constants;
|
||||
import android.inputmethodservice.cts.common.Ime2Constants;
|
||||
import android.inputmethodservice.cts.common.test.DeviceTestConstants;
|
||||
import android.inputmethodservice.cts.common.test.ShellCommandUtils;
|
||||
import android.inputmethodservice.cts.devicetest.SequenceMatcher.MatchResult;
|
||||
import android.os.SystemClock;
|
||||
import android.widget.SearchView;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class InputMethodServiceDeviceTest {
|
||||
|
||||
private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
|
||||
|
||||
/** Test to check CtsInputMethod1 receives onCreate and onStartInput. */
|
||||
@Test
|
||||
public void testCreateIme1() throws Throwable {
|
||||
final TestHelper helper = new TestHelper(getClass(), DeviceTestConstants.TEST_CREATE_IME1);
|
||||
|
||||
pollingCheck(() -> helper.queryAllEvents()
|
||||
.collect(startingFrom(helper.isStartOfTest()))
|
||||
.filter(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE)))
|
||||
.findAny().isPresent(),
|
||||
TIMEOUT, "CtsInputMethod1.onCreate is called");
|
||||
|
||||
final long startActivityTime = SystemClock.uptimeMillis();
|
||||
helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
|
||||
|
||||
pollingCheck(() -> helper.queryAllEvents()
|
||||
.filter(isNewerThan(startActivityTime))
|
||||
.filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
|
||||
.findAny().isPresent(),
|
||||
TIMEOUT, "CtsInputMethod1.onStartInput is called");
|
||||
}
|
||||
|
||||
/** Test to check IME is switched from CtsInputMethod1 to CtsInputMethod2. */
|
||||
@Test
|
||||
public void testSwitchIme1ToIme2() throws Throwable {
|
||||
final TestHelper helper = new TestHelper(
|
||||
getClass(), DeviceTestConstants.TEST_SWITCH_IME1_TO_IME2);
|
||||
|
||||
pollingCheck(() -> helper.queryAllEvents()
|
||||
.collect(startingFrom(helper.isStartOfTest()))
|
||||
.filter(isFrom(Ime1Constants.CLASS).and(isType(ON_CREATE)))
|
||||
.findAny().isPresent(),
|
||||
TIMEOUT, "CtsInputMethod1.onCreate is called");
|
||||
|
||||
final long startActivityTime = SystemClock.uptimeMillis();
|
||||
helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
|
||||
|
||||
pollingCheck(() -> helper.queryAllEvents()
|
||||
.filter(isNewerThan(startActivityTime))
|
||||
.filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
|
||||
.findAny().isPresent(),
|
||||
TIMEOUT, "CtsInputMethod1.onStartInput is called");
|
||||
|
||||
helper.findUiObject(R.id.text_entry).click();
|
||||
|
||||
// Switch IME from CtsInputMethod1 to CtsInputMethod2.
|
||||
final long switchImeTime = SystemClock.uptimeMillis();
|
||||
helper.shell(ShellCommandUtils.broadcastIntent(
|
||||
ACTION_IME_COMMAND, Ime1Constants.PACKAGE,
|
||||
"-e", EXTRA_COMMAND, COMMAND_SWITCH_INPUT_METHOD,
|
||||
"-e", EXTRA_ARG_STRING1, Ime2Constants.IME_ID));
|
||||
|
||||
pollingCheck(() -> helper.shell(ShellCommandUtils.getCurrentIme())
|
||||
.equals(Ime2Constants.IME_ID),
|
||||
TIMEOUT, "CtsInputMethod2 is current IME");
|
||||
pollingCheck(() -> helper.queryAllEvents()
|
||||
.filter(isNewerThan(switchImeTime))
|
||||
.filter(isFrom(Ime1Constants.CLASS).and(isType(ON_DESTROY)))
|
||||
.findAny().isPresent(),
|
||||
TIMEOUT, "CtsInputMethod1.onDestroy is called");
|
||||
pollingCheck(() -> helper.queryAllEvents()
|
||||
.filter(isNewerThan(switchImeTime))
|
||||
.filter(isFrom(Ime2Constants.CLASS))
|
||||
.collect(sequenceOfTypes(ON_CREATE, ON_START_INPUT))
|
||||
.matched(),
|
||||
TIMEOUT,
|
||||
"CtsInputMethod2.onCreate and onStartInput are called in sequence");
|
||||
}
|
||||
|
||||
/** Test to check CtsInputMethod1 isn't current IME. */
|
||||
@Test
|
||||
public void testIme1IsNotCurrentIme() throws Throwable {
|
||||
final TestHelper helper =
|
||||
new TestHelper(getClass(), DeviceTestConstants.TEST_IME1_IS_NOT_CURRENT_IME);
|
||||
|
||||
helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
|
||||
helper.findUiObject(R.id.text_entry).click();
|
||||
|
||||
pollingCheck(() -> !helper.shell(ShellCommandUtils.getCurrentIme())
|
||||
.equals(Ime1Constants.IME_ID),
|
||||
TIMEOUT,
|
||||
"CtsInputMethod1 is uninstalled or disabled, and current IME becomes other IME");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchView_giveFocusShowIme1() throws Throwable {
|
||||
final TestHelper helper = new TestHelper(
|
||||
getClass(), DeviceTestConstants.TEST_SEARCH_VIEW_GIVE_FOCUS_SHOW_IME1);
|
||||
|
||||
helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
|
||||
helper.findUiObject(R.id.search_view).click();
|
||||
pollingCheck(() -> helper.queryAllEvents()
|
||||
.collect(startingFrom(helper.isStartOfTest()))
|
||||
.filter(isFrom(Ime1Constants.CLASS).and(isType(SHOW_SOFT_INPUT)))
|
||||
.findAny().isPresent(),
|
||||
TIMEOUT, "CtsInputMethod1.showSoftInput is called");
|
||||
pollingCheck(() -> helper.queryAllEvents()
|
||||
.collect(startingFrom(helper.isStartOfTest()))
|
||||
.filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
|
||||
.findAny().isPresent(),
|
||||
TIMEOUT, "CtsInputMethod1.onStartInput is called");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchView_setQueryHideIme1() throws Throwable {
|
||||
final TestHelper helper = new TestHelper(
|
||||
getClass(), DeviceTestConstants.TEST_SEARCH_VIEW_SET_QUERY_HIDE_IME1);
|
||||
|
||||
final Activity activity = helper.launchActivitySync(
|
||||
DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
|
||||
final SearchView searchView = (SearchView) activity.findViewById(R.id.search_view);
|
||||
helper.findUiObject(R.id.search_view).click();
|
||||
// test SearchView.onSubmitQuery() closes IME. Alternatively, onCloseClicked() closes IME.
|
||||
// submits the query, should dismiss the inputMethod.
|
||||
activity.runOnUiThread(() -> searchView.setQuery("test", true /* submit */));
|
||||
|
||||
pollingCheck(() -> helper.queryAllEvents()
|
||||
.collect(startingFrom(helper.isStartOfTest()))
|
||||
.filter(isFrom(Ime1Constants.CLASS).and(isType(ON_FINISH_INPUT)))
|
||||
.findAny().isPresent(),
|
||||
TIMEOUT, "CtsInputMethod1.onFinishInput is called");
|
||||
pollingCheck(() -> helper.queryAllEvents()
|
||||
.collect(startingFrom(helper.isStartOfTest()))
|
||||
.filter(isFrom(Ime1Constants.CLASS).and(isType(HIDE_SOFT_INPUT)))
|
||||
.findAny().isPresent(),
|
||||
TIMEOUT, "CtsInputMethod1.hideSoftInput is called");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartInputCalledOnceIme1() throws Exception {
|
||||
final TestHelper helper = new TestHelper(
|
||||
getClass(), DeviceTestConstants.TEST_ON_START_INPUT_CALLED_ONCE_IME1);
|
||||
|
||||
helper.launchActivity(DeviceTestConstants.PACKAGE, DeviceTestConstants.TEST_ACTIVITY_CLASS);
|
||||
helper.findUiObject(R.id.text_entry).click();
|
||||
|
||||
// we should've only one onStartInput call.
|
||||
pollingCheck(() -> helper.queryAllEvents()
|
||||
.collect(startingFrom(helper.isStartOfTest()))
|
||||
.filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
|
||||
.findAny()
|
||||
.isPresent(),
|
||||
TIMEOUT, "CtsInputMethod1.onStartInput is called");
|
||||
List<DeviceEvent> startInputEvents = helper.queryAllEvents()
|
||||
.collect(startingFrom(helper.isStartOfTest()))
|
||||
.filter(isFrom(Ime1Constants.CLASS).and(isType(ON_START_INPUT)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertEquals("CtsInputMethod1.onStartInput is called exactly once",
|
||||
startInputEvents.size(),
|
||||
1);
|
||||
|
||||
// check if that single event didn't cause IME restart.
|
||||
final DeviceEvent event = startInputEvents.get(0);
|
||||
Boolean isRestarting = DeviceEvent.getEventParamBoolean(
|
||||
DeviceEventTypeParam.ON_START_INPUT_RESTARTING, event);
|
||||
assertTrue(isRestarting != null);
|
||||
assertFalse(isRestarting);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build stream collector of {@link DeviceEvent} collecting sequence that elements have
|
||||
* specified types.
|
||||
*
|
||||
* @param types {@link DeviceEventType}s that elements of sequence should have.
|
||||
* @return {@link java.util.stream.Collector} that corrects the sequence.
|
||||
*/
|
||||
private static Collector<DeviceEvent, ?, MatchResult<DeviceEvent>> sequenceOfTypes(
|
||||
final DeviceEventType... types) {
|
||||
final IntFunction<Predicate<DeviceEvent>[]> arraySupplier = Predicate[]::new;
|
||||
return SequenceMatcher.of(Arrays.stream(types)
|
||||
.map(DeviceEvent::isType)
|
||||
.toArray(arraySupplier));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.devicetest;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class InputMethodServiceTestActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_inputmethod_test);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package android.inputmethodservice.cts.devicetest;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.Stream.Builder;
|
||||
|
||||
/**
|
||||
* More stream collectors.
|
||||
*/
|
||||
final class MoreCollectors {
|
||||
|
||||
/**
|
||||
* Create a collector that collects elements ending at an element that satisfies specified
|
||||
* predicate. For example,
|
||||
* <pre>
|
||||
* Stream.of("a", "b", "c", "d", "c", "d", "e").collect(endingAt(s -> s.equals("d")))
|
||||
* </pre>
|
||||
* returns {@code Stream.of("a", "b", "c", "d")}.
|
||||
*
|
||||
* @param predicate a predicator to find a specific element.
|
||||
* @param <E> a type of element.
|
||||
* @return {@link Collector} object that collects elements ending at an element that is
|
||||
* accepted by {@code predicate}.
|
||||
*/
|
||||
static <E> Collector<E, ?, Stream<E>> endingAt(final Predicate<E> predicate) {
|
||||
final BiConsumer<Builder<E>, E> endingAtAccumulator = new BiConsumer<Builder<E>, E>() {
|
||||
private boolean mFound = false;
|
||||
|
||||
@Override
|
||||
public void accept(final Builder<E> builder, final E element) {
|
||||
if (mFound) {
|
||||
return;
|
||||
}
|
||||
if (predicate.test(element)) {
|
||||
mFound = true;
|
||||
}
|
||||
builder.accept(element);
|
||||
}
|
||||
};
|
||||
return Collector.of(
|
||||
Stream::builder,
|
||||
endingAtAccumulator,
|
||||
(builder, builder2) -> {
|
||||
throw new UnsupportedOperationException("Do not use on parallel stream.");
|
||||
},
|
||||
Builder::build);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a collector that collects elements starting from an element that satisfies specified
|
||||
* predicate. For example,
|
||||
* <pre>
|
||||
* Stream.of("a", "b", "c", "d", "c", "d", "e").collect(startingFrom(s -> s.equals("d")))
|
||||
* </pre>
|
||||
* returns {@code Stream.of("d", "c", "d", "e")}.
|
||||
*
|
||||
* @param predicate a predicator to find a specific element.
|
||||
* @param <E> a type of element.
|
||||
* @return {@link Collector} object that collects elements starting from an element that is
|
||||
* accepted by {@code predicate}.
|
||||
*/
|
||||
static <E> Collector<E, ?, Stream<E>> startingFrom(final Predicate<E> predicate) {
|
||||
final BiConsumer<Builder<E>, E> startingFromAccumulator = new BiConsumer<Builder<E>, E>() {
|
||||
private boolean mFound = false;
|
||||
|
||||
@Override
|
||||
public void accept(final Builder<E> builder, final E element) {
|
||||
if (mFound) {
|
||||
builder.accept(element);
|
||||
return;
|
||||
}
|
||||
if (predicate.test(element)) {
|
||||
mFound = true;
|
||||
builder.accept(element);
|
||||
}
|
||||
}
|
||||
};
|
||||
return Collector.of(
|
||||
Stream::builder,
|
||||
startingFromAccumulator,
|
||||
(builder, builder2) -> {
|
||||
throw new UnsupportedOperationException("Do not use on parallel stream.");
|
||||
},
|
||||
Builder::build);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package android.inputmethodservice.cts.devicetest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collector;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Sequence matcher on {@link Stream}.
|
||||
*/
|
||||
final class SequenceMatcher {
|
||||
|
||||
/**
|
||||
* Return type of this matcher.
|
||||
* @param <E> type of stream element.
|
||||
*/
|
||||
static final class MatchResult<E> {
|
||||
|
||||
private final boolean mMatched;
|
||||
private final List<E> mMatchedSequence;
|
||||
|
||||
MatchResult(final boolean matched, final List<E> matchedSequence) {
|
||||
mMatched = matched;
|
||||
mMatchedSequence = matchedSequence;
|
||||
}
|
||||
|
||||
boolean matched() {
|
||||
return mMatched;
|
||||
}
|
||||
|
||||
List<E> getMatchedSequence() {
|
||||
return mMatchedSequence;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulate continuous sequence of elements that satisfy specified {@link Predicate}s.
|
||||
* @param <E> type of stream element.
|
||||
*/
|
||||
private static final class SequenceAccumulator<E> {
|
||||
|
||||
private final Predicate<E>[] mPredicates;
|
||||
private final List<E> mSequence = new ArrayList<>();
|
||||
|
||||
SequenceAccumulator(final Predicate<E>... predicates) {
|
||||
mPredicates = predicates;
|
||||
}
|
||||
|
||||
void accumulate(final E element) {
|
||||
if (mSequence.isEmpty()) {
|
||||
// Search for the first element of sequence.
|
||||
if (mPredicates[0].test(element)) {
|
||||
mSequence.add(element);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final int currentIndex = mSequence.size();
|
||||
if (currentIndex >= mPredicates.length) {
|
||||
// Already found sequence.
|
||||
return;
|
||||
}
|
||||
if (mPredicates[currentIndex].test(element)) {
|
||||
mSequence.add(element);
|
||||
} else {
|
||||
// Not continuous, restart searching from the first.
|
||||
mSequence.clear();
|
||||
}
|
||||
}
|
||||
|
||||
MatchResult<E> getResult() {
|
||||
return new MatchResult<>(mSequence.size() == mPredicates.length, mSequence);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@link Collector} that collects continuous sequence of elements that equal to
|
||||
* specified {@code elements}. It returns {@link MatchResult<E>} of found such sequence.
|
||||
* <pre>
|
||||
* Stream.of(1, 2, 3, 4, 5).collect(
|
||||
* SequenceMatcher.of(1)).matched(); // true
|
||||
* Stream.of(1, 2, 3, 4, 5).collect(
|
||||
* SequenceMatcher.of(2)).matched(); // true
|
||||
* Stream.of(1, 2, 3, 4, 5).collect(
|
||||
* SequenceMatcher.of(0)).matched(); // false
|
||||
* Stream.of(1, 2, 3, 4, 5).collect(
|
||||
* SequenceMatcher.of(2, 3, 4)).matched(); // true
|
||||
* Stream.of(1, 2, 3, 4, 5).collect(
|
||||
* SequenceMatcher.of(2, 3, 5)).matched(); // false, not continuous.
|
||||
* Stream.of(1, 2, 3, 4, 5).collect(
|
||||
* SequenceMatcher.of(2, 1)).matched(); // false
|
||||
* Stream.of(1, 2, 3, 4, 5).collect(
|
||||
* SequenceMatcher.of(1, 2, 3, 4, 5, 6)).matched(); // false
|
||||
* Stream.of(1, 1, 1, 1, 1).collect(
|
||||
* SequenceMatcher.of(1, 1)).matched(); // true
|
||||
* Stream.of(1, 1, 1, 1, 1).collect(
|
||||
* SequenceMatcher.of(1, 1, 1)).matched(); // true
|
||||
* Stream.of(1, 1, 1, 1, 1).collect(
|
||||
* SequenceMatcher.of(1, 1, 1, 1, 1, 1)).matched(); // false
|
||||
* Stream.of(1, 1, 0, 1, 1).collect(
|
||||
* SequenceMatcher.of(1, 1, 1)).matched(); // false, not continuous.
|
||||
* </pre>
|
||||
*
|
||||
* @param elements elements of matching sequence.
|
||||
* @param <E> type of stream element
|
||||
* @return {@link MatchResult<E>} of matcher sequence.
|
||||
*/
|
||||
static <E> Collector<E, ?, MatchResult<E>> of(final E... elements) {
|
||||
if (elements == null || elements.length == 0) {
|
||||
throw new IllegalArgumentException("At least one element.");
|
||||
}
|
||||
final IntFunction<Predicate<E>[]> arraySupplier = Predicate[]::new;
|
||||
return of(Arrays.stream(elements).map(Predicate::isEqual).toArray(arraySupplier));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Collector} that collects continuous sequence of elements that satisfied
|
||||
* specified {@code predicates}. It returns {@link MatchResult<E>} of found such sequence.
|
||||
* <p>Please see examples in {@link #of(Object...)}.
|
||||
*
|
||||
* @param predicates array of {@link Predicate<E>} that each of sequence element should satisfy.
|
||||
* @param <E> type of stream element.
|
||||
* @return {@link MatchResult<E>} of matched sequence.
|
||||
*/
|
||||
static <E> Collector<E, ?, MatchResult<E>> of(final Predicate<E>... predicates) {
|
||||
if (predicates == null || predicates.length == 0) {
|
||||
throw new IllegalArgumentException("At least one Predicate.");
|
||||
}
|
||||
return Collector.of(
|
||||
() -> new SequenceAccumulator<>(predicates),
|
||||
SequenceAccumulator::accumulate,
|
||||
(accumulate, accumulate2) -> {
|
||||
throw new UnsupportedOperationException("Do not use on parallel stream.");
|
||||
},
|
||||
SequenceAccumulator::getResult);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.devicetest;
|
||||
|
||||
import static android.inputmethodservice.cts.DeviceEvent.isFrom;
|
||||
import static android.inputmethodservice.cts.DeviceEvent.isType;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.TEST_START;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.inputmethodservice.cts.DeviceEvent;
|
||||
import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType;
|
||||
import android.inputmethodservice.cts.common.EventProviderConstants.EventTableConstants;
|
||||
import android.inputmethodservice.cts.common.test.TestInfo;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.IdRes;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
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 java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Helper object for device side test.
|
||||
*/
|
||||
final class TestHelper {
|
||||
|
||||
/** Content URI of device event provider. */
|
||||
private static final Uri DEVICE_EVENTS_CONTENT_URI = Uri.parse(EventTableConstants.CONTENT_URI);
|
||||
private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
|
||||
|
||||
private final TestInfo mTestInfo;
|
||||
private final ContentResolver mResolver;
|
||||
private final Context mTargetContext;
|
||||
private final UiDevice mUiDevice;
|
||||
private final Instrumentation mInstrumentation;
|
||||
|
||||
/**
|
||||
* Construct a helper object of specified test method.
|
||||
*
|
||||
* @param testClass a {@link Class} of test.
|
||||
* @param testMethod a name of test method.
|
||||
*/
|
||||
TestHelper(final Class<?> testClass, final String testMethod) {
|
||||
final Context testContext = InstrumentationRegistry.getContext();
|
||||
mTestInfo = new TestInfo(testContext.getPackageName(), testClass.getName(), testMethod);
|
||||
mResolver = testContext.getContentResolver();
|
||||
mTargetContext = InstrumentationRegistry.getTargetContext();
|
||||
mInstrumentation = InstrumentationRegistry.getInstrumentation();
|
||||
mUiDevice = UiDevice.getInstance(mInstrumentation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a shell {@code command} and return its standard out.
|
||||
* @param command a shell command text to execute.
|
||||
* @return command's standard output without ending newline.
|
||||
* @throws IOException
|
||||
*/
|
||||
String shell(final String command) throws IOException {
|
||||
return mUiDevice.executeShellCommand(command).trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Launching an Activity for test, and wait for completions of launch.
|
||||
* @param packageName activity's app package name.
|
||||
* @param className activity's class name.
|
||||
*/
|
||||
void launchActivity(final String packageName, final String className) {
|
||||
final Intent intent = new Intent()
|
||||
.setAction(Intent.ACTION_MAIN)
|
||||
.setClassName(packageName, className)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
InstrumentationRegistry.getContext().startActivity(intent);
|
||||
mUiDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an UI element from resource ID.
|
||||
* @param resId id of finding UI element.
|
||||
* @return {@link UiObject2} of found UI element.
|
||||
*/
|
||||
UiObject2 findUiObject(@IdRes int resId) {
|
||||
final String resourceName = mTargetContext.getResources().getResourceName(resId);
|
||||
return mUiDevice.findObject(By.res(resourceName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch test activity synchronously.
|
||||
*
|
||||
* @param packageName activity's app package name.
|
||||
* @param className activity's class name.
|
||||
* @return instance of Activity
|
||||
*/
|
||||
Activity launchActivitySync(final String packageName, final String className) {
|
||||
final Intent intent = new Intent()
|
||||
.setAction(Intent.ACTION_MAIN)
|
||||
.setClassName(packageName, className)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
return mInstrumentation.startActivitySync(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all device events as {@link Stream}
|
||||
* @return {@link Stream<DeviceEvent>} of all device events.
|
||||
*/
|
||||
Stream<DeviceEvent> queryAllEvents() {
|
||||
try (final Cursor cursor = mResolver.query(
|
||||
DEVICE_EVENTS_CONTENT_URI,
|
||||
null /* projection */,
|
||||
null /* selection */,
|
||||
null /* selectionArgs */,
|
||||
null /* sortOrder */)) {
|
||||
return DeviceEvent.buildStream(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a {@link Predicate} can be used for skipping device events in {@link Stream} until
|
||||
* {@link DeviceEventType#TEST_START TEST_START} device event of this test method.
|
||||
* @return {@llink Predicate<DeviceEvent>} that return true after accepting
|
||||
* {@link DeviceEventType#TEST_START TEST_START} of this test method.
|
||||
*/
|
||||
Predicate<DeviceEvent> isStartOfTest() {
|
||||
return isFrom(mTestInfo.getTestName()).and(isType(TEST_START));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
# Copyright (C) 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# Don't include this package in any target
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
# When built, explicitly put it in the data partition.
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
|
||||
|
||||
LOCAL_DEX_PREOPT := false
|
||||
|
||||
LOCAL_PROGUARD_ENABLED := disabled
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_JAVA_RESOURCE_DIR := res
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
CtsInputMethodServiceCommon \
|
||||
CtsInputMethodServiceLib
|
||||
|
||||
# tag this module as a cts test artifact
|
||||
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
|
||||
|
||||
LOCAL_PACKAGE_NAME := CtsInputMethod1
|
||||
|
||||
LOCAL_SDK_VERSION := current
|
||||
|
||||
include $(BUILD_PACKAGE)
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="android.inputmethodservice.cts.ime1">
|
||||
|
||||
<!--
|
||||
TODO: We may need to have another new APK that has the latest targetSdkVersion to check the
|
||||
latest OS behaviors.
|
||||
-->
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25" />
|
||||
|
||||
<application
|
||||
android:label="@string/ime_name"
|
||||
android:allowBackup="false"
|
||||
android:theme="@android:style/Theme.InputMethod"
|
||||
>
|
||||
<service
|
||||
android:name=".CtsInputMethod1"
|
||||
android:label="@string/ime_name"
|
||||
android:permission="android.permission.BIND_INPUT_METHOD">
|
||||
<intent-filter>
|
||||
<action android:name="android.view.InputMethod" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.view.im"
|
||||
android:resource="@xml/ime1" />
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/holo_orange_dark"
|
||||
android:padding="80dp"
|
||||
>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:textSize="72sp"
|
||||
android:gravity="center"
|
||||
android:id="@+id/ime_name"
|
||||
android:text="@string/ime_name" />
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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>
|
||||
<color name="input_view_background">@android:color/holo_orange_dark</color>
|
||||
</resources>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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>
|
||||
<string name="ime_name" translatable="false">IME1</string>
|
||||
</resources>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:supportsSwitchingToNextInputMethod="true">
|
||||
</input-method>
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.ime1;
|
||||
|
||||
import android.inputmethodservice.cts.ime.CtsBaseInputMethod;
|
||||
import android.view.View;
|
||||
|
||||
public final class CtsInputMethod1 extends CtsBaseInputMethod {
|
||||
|
||||
@Override
|
||||
public View onCreateInputView() {
|
||||
return getLayoutInflater().inflate(R.layout.input_view, null /* root */);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
# Copyright (C) 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# Don't include this package in any target
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
# When built, explicitly put it in the data partition.
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
|
||||
|
||||
LOCAL_DEX_PREOPT := false
|
||||
|
||||
LOCAL_PROGUARD_ENABLED := disabled
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_JAVA_RESOURCE_DIR := res
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
CtsInputMethodServiceCommon \
|
||||
CtsInputMethodServiceLib
|
||||
|
||||
# tag this module as a cts test artifact
|
||||
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
|
||||
|
||||
LOCAL_PACKAGE_NAME := CtsInputMethod2
|
||||
|
||||
LOCAL_SDK_VERSION := current
|
||||
|
||||
include $(BUILD_PACKAGE)
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="android.inputmethodservice.cts.ime2">
|
||||
|
||||
<!--
|
||||
TODO: We may need to have another new APK that has the latest targetSdkVersion to check the
|
||||
latest OS behaviors.
|
||||
-->
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25" />
|
||||
|
||||
<application
|
||||
android:label="@string/ime_name"
|
||||
android:allowBackup="false"
|
||||
android:theme="@android:style/Theme.InputMethod"
|
||||
>
|
||||
<service
|
||||
android:name=".CtsInputMethod2"
|
||||
android:label="@string/ime_name"
|
||||
android:permission="android.permission.BIND_INPUT_METHOD">
|
||||
<intent-filter>
|
||||
<action android:name="android.view.InputMethod" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.view.im"
|
||||
android:resource="@xml/ime2" />
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/holo_blue_dark"
|
||||
android:padding="80dp"
|
||||
>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:textSize="72sp"
|
||||
android:gravity="center"
|
||||
android:id="@+id/ime_name"
|
||||
android:text="@string/ime_name" />
|
||||
</RelativeLayout>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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>
|
||||
<color name="input_view_background">@android:color/holo_blue_dark</color>
|
||||
</resources>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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>
|
||||
<string name="ime_name" translatable="false">IME2</string>
|
||||
</resources>
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:supportsSwitchingToNextInputMethod="true">
|
||||
</input-method>
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.ime2;
|
||||
|
||||
import android.inputmethodservice.cts.ime.CtsBaseInputMethod;
|
||||
import android.view.View;
|
||||
|
||||
public final class CtsInputMethod2 extends CtsBaseInputMethod {
|
||||
|
||||
@Override
|
||||
public View onCreateInputView() {
|
||||
return getLayoutInflater().inflate(R.layout.input_view, null /* root */);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
# Copyright (C) 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
android-support-annotations \
|
||||
json \
|
||||
CtsInputMethodServiceCommon
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
# tag this module as a cts test artifact
|
||||
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
|
||||
|
||||
LOCAL_MODULE := CtsInputMethodServiceLib
|
||||
|
||||
LOCAL_SDK_VERSION := current
|
||||
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
|
@ -0,0 +1,354 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts;
|
||||
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.ACTION_DEVICE_EVENT;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_PARAMS;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_TIME;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_TYPE;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_SENDER;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVER_CLASS;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVER_PACKAGE;
|
||||
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.inputmethodservice.cts.common.DeviceEventConstants;
|
||||
import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType;
|
||||
import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventTypeParam;
|
||||
import android.inputmethodservice.cts.common.EventProviderConstants.EventTableConstants;
|
||||
import android.inputmethodservice.cts.common.test.TestInfo;
|
||||
import android.inputmethodservice.cts.db.Entity;
|
||||
import android.inputmethodservice.cts.db.Field;
|
||||
import android.inputmethodservice.cts.db.Table;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.json.stream.JsonReader;
|
||||
import com.android.json.stream.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Device event object.
|
||||
* <p>Device event is one of IME event and Test event, and is used to test behaviors of Input Method
|
||||
* Framework.</p>
|
||||
*/
|
||||
public final class DeviceEvent {
|
||||
|
||||
private static final boolean DEBUG_STREAM = false;
|
||||
|
||||
public static final Table<DeviceEvent> TABLE = new DeviceEventTable(EventTableConstants.NAME);
|
||||
|
||||
public static IntentBuilder builder() {
|
||||
return new IntentBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder to create an intent to send a device event.
|
||||
* The built intent that has event {@code sender}, {@code type}, {@code paramsString}, time from
|
||||
* {@link SystemClock#uptimeMillis()}, and target component of event receiver.
|
||||
*
|
||||
*/
|
||||
public static final class IntentBuilder {
|
||||
String mSender;
|
||||
DeviceEventType mType;
|
||||
JsonWriter mJsonWriter;
|
||||
StringWriter mStringWriter;
|
||||
|
||||
/**
|
||||
* @param type an event type defined at {@link DeviceEventType}.
|
||||
*/
|
||||
public IntentBuilder setType(DeviceEventType type) {
|
||||
mType = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sender an event sender.
|
||||
*/
|
||||
public void setSender(String sender) {
|
||||
mSender = sender;
|
||||
}
|
||||
|
||||
public IntentBuilder with(DeviceEventTypeParam eventParam, boolean value) {
|
||||
appendToJson(eventParam, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Intent build() {
|
||||
Intent intent = new Intent()
|
||||
.setAction(ACTION_DEVICE_EVENT)
|
||||
.setClassName(RECEIVER_PACKAGE, RECEIVER_CLASS)
|
||||
.putExtra(EXTRA_EVENT_SENDER, mSender)
|
||||
.putExtra(EXTRA_EVENT_TYPE, mType.name())
|
||||
.putExtra(EXTRA_EVENT_PARAMS, getJsonString())
|
||||
.putExtra(EXTRA_EVENT_TIME, SystemClock.uptimeMillis());
|
||||
|
||||
mJsonWriter = null;
|
||||
mStringWriter = null;
|
||||
return intent;
|
||||
}
|
||||
|
||||
private String getJsonString() {
|
||||
if (mJsonWriter == null) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
mJsonWriter.endObject();
|
||||
mJsonWriter.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("IntentBuilder.getJsonString() failed.", e);
|
||||
}
|
||||
return mStringWriter.toString();
|
||||
}
|
||||
|
||||
private void appendToJson(DeviceEventTypeParam eventParam, boolean value) {
|
||||
final String key = eventParam.getName();
|
||||
if (TextUtils.isEmpty(key)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (mJsonWriter == null) {
|
||||
mStringWriter = new StringWriter();
|
||||
mJsonWriter = new JsonWriter(mStringWriter);
|
||||
mJsonWriter.beginObject();
|
||||
}
|
||||
mJsonWriter.name(key).value(value);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("IntentBuilder.appendToJson() failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link DeviceEvent} object from an intent.
|
||||
* @param intent a device event intent defined at {@link DeviceEventConstants}.
|
||||
* @return {@link DeviceEvent} object that has event sender, type, and time form an
|
||||
* {@code intent}.
|
||||
*/
|
||||
public static DeviceEvent newEvent(final Intent intent) {
|
||||
final String sender = intent.getStringExtra(EXTRA_EVENT_SENDER);
|
||||
if (sender == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Intent must have " + EXTRA_EVENT_SENDER + ": " + intent);
|
||||
}
|
||||
|
||||
final String typeString = intent.getStringExtra(EXTRA_EVENT_TYPE);
|
||||
if (typeString == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Intent must have " + EXTRA_EVENT_TYPE + ": " + intent);
|
||||
}
|
||||
final DeviceEventType type = DeviceEventType.valueOf(typeString);
|
||||
|
||||
if (!intent.hasExtra(EXTRA_EVENT_TIME)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Intent must have " + EXTRA_EVENT_TIME + ": " + intent);
|
||||
}
|
||||
|
||||
String paramsString = intent.getStringExtra(DeviceEventConstants.EXTRA_EVENT_PARAMS);
|
||||
if (paramsString == null) {
|
||||
paramsString = "";
|
||||
}
|
||||
|
||||
return new DeviceEvent(
|
||||
sender,
|
||||
type,
|
||||
paramsString,
|
||||
intent.getLongExtra(EXTRA_EVENT_TIME, 0L));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build {@link ContentValues} object from {@link DeviceEvent} object.
|
||||
* @param event a {@link DeviceEvent} object to be converted.
|
||||
* @return a converted {@link ContentValues} object.
|
||||
*/
|
||||
public static ContentValues buildContentValues(final DeviceEvent event) {
|
||||
return TABLE.buildContentValues(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build {@link Stream<DeviceEvent>} object from {@link Cursor} comes from Content Provider.
|
||||
* @param cursor a {@link Cursor} object to be converted.
|
||||
* @return a converted {@link Stream<DeviceEvent>} object.
|
||||
*/
|
||||
public static Stream<DeviceEvent> buildStream(final Cursor cursor) {
|
||||
return TABLE.buildStream(cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build {@link Predicate<DeviceEvent>} whether a device event comes from {@code sender}
|
||||
*
|
||||
* @param sender event sender.
|
||||
* @return {@link Predicate<DeviceEvent>} object.
|
||||
*/
|
||||
public static Predicate<DeviceEvent> isFrom(final String sender) {
|
||||
return e -> e.sender.equals(sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build {@link Predicate<DeviceEvent>} whether a device event has an event {@code type}.
|
||||
*
|
||||
* @param type a event type defined in {@link DeviceEventType}.
|
||||
* @return {@link Predicate<DeviceEvent>} object.
|
||||
*/
|
||||
public static Predicate<DeviceEvent> isType(final DeviceEventType type) {
|
||||
return e -> e.type == type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build {@link Predicate<DeviceEvent>} whether a device event is newer than or equals to
|
||||
* {@code time}.
|
||||
*
|
||||
* @param time a time to compare against.
|
||||
* @return {@link Predicate<DeviceEvent>} object.
|
||||
*/
|
||||
public static Predicate<DeviceEvent> isNewerThan(final long time) {
|
||||
return e -> e.time >= time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event source, either Input Method class name or {@link TestInfo#getTestName()}.
|
||||
*/
|
||||
@NonNull
|
||||
public final String sender;
|
||||
|
||||
/**
|
||||
* Event type, either IME event or Test event.
|
||||
*/
|
||||
@NonNull
|
||||
public final DeviceEventType type;
|
||||
|
||||
/**
|
||||
* Event parameters formatted as json string.
|
||||
* e.g. {@link DeviceEventTypeParam#ON_START_INPUT_RESTARTING}
|
||||
*/
|
||||
public final String paramsString;
|
||||
|
||||
/**
|
||||
* Event time, value is from {@link SystemClock#uptimeMillis()}.
|
||||
*/
|
||||
public final long time;
|
||||
|
||||
private DeviceEvent(
|
||||
final String sender, final DeviceEventType type, String paramsString, final long time) {
|
||||
this.sender = sender;
|
||||
this.type = type;
|
||||
this.paramsString = paramsString;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Event{ time:" + time + " type:" + type + " sender:" + sender + " }";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param eventParam {@link DeviceEventTypeParam} to look for.
|
||||
* @param event {@link DeviceEvent} to look in.
|
||||
* @return Event parameter for provided key. If key is not found in
|
||||
* {@link DeviceEvent#paramsString}, null is returned.
|
||||
*
|
||||
* TODO: Support other primitive and custom types.
|
||||
*/
|
||||
@Nullable
|
||||
public static Boolean getEventParamBoolean(
|
||||
DeviceEventTypeParam eventParam, final DeviceEvent event) {
|
||||
StringReader stringReader = new StringReader(event.paramsString);
|
||||
JsonReader reader = new JsonReader(stringReader);
|
||||
|
||||
try {
|
||||
reader.beginObject();
|
||||
while (reader.hasNext()) {
|
||||
String name = reader.nextName();
|
||||
if (name.equals(eventParam.getName())) {
|
||||
Boolean value = reader.nextBoolean();
|
||||
reader.endObject();
|
||||
return value;
|
||||
}
|
||||
}
|
||||
reader.endObject();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("DeviceEvent.getEventParamBoolean() failed.", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction of device event table in database.
|
||||
*/
|
||||
private static final class DeviceEventTable extends Table<DeviceEvent> {
|
||||
|
||||
private static final String LOG_TAG = DeviceEventTable.class.getSimpleName();
|
||||
|
||||
private final Field SENDER;
|
||||
private final Field TYPE;
|
||||
private final Field TIME;
|
||||
private final Field PARAMS;
|
||||
|
||||
private DeviceEventTable(final String name) {
|
||||
super(name, new Entity.Builder<DeviceEvent>()
|
||||
.addField(EventTableConstants.SENDER, Cursor.FIELD_TYPE_STRING)
|
||||
.addField(EventTableConstants.TYPE, Cursor.FIELD_TYPE_STRING)
|
||||
.addField(EventTableConstants.TIME, Cursor.FIELD_TYPE_INTEGER)
|
||||
.addField(EventTableConstants.EXTRAS, Cursor.FIELD_TYPE_STRING)
|
||||
.build());
|
||||
SENDER = getField(EventTableConstants.SENDER);
|
||||
TYPE = getField(EventTableConstants.TYPE);
|
||||
TIME = getField(EventTableConstants.TIME);
|
||||
PARAMS = getField(EventTableConstants.EXTRAS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentValues buildContentValues(final DeviceEvent event) {
|
||||
final ContentValues values = new ContentValues();
|
||||
SENDER.putString(values, event.sender);
|
||||
TYPE.putString(values, event.type.name());
|
||||
PARAMS.putString(values, event.paramsString);
|
||||
TIME.putLong(values, event.time);
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<DeviceEvent> buildStream(Cursor cursor) {
|
||||
if (DEBUG_STREAM) {
|
||||
Log.d(LOG_TAG, "buildStream:");
|
||||
}
|
||||
final Stream.Builder<DeviceEvent> builder = Stream.builder();
|
||||
for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
|
||||
final DeviceEvent event = new DeviceEvent(
|
||||
SENDER.getString(cursor),
|
||||
DeviceEventType.valueOf(TYPE.getString(cursor)),
|
||||
PARAMS.getString(cursor),
|
||||
TIME.getLong(cursor));
|
||||
builder.accept(event);
|
||||
if (DEBUG_STREAM) {
|
||||
Log.d(LOG_TAG, " event=" +event);
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.db;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Abstraction of SQLite database.
|
||||
*/
|
||||
public abstract class Database {
|
||||
|
||||
private final SQLiteOpenHelper mHelper;
|
||||
|
||||
public Database(final Context context, final String name, final int version) {
|
||||
mHelper = new SQLiteOpenHelper(context, name, null /* factory */, version) {
|
||||
@Override
|
||||
public void onCreate(final SQLiteDatabase db) {
|
||||
db.beginTransaction();
|
||||
try {
|
||||
for (final Table table : getTables()) {
|
||||
db.execSQL(table.createTableSql());
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(final SQLiteDatabase db, final int oldVersion,
|
||||
final int newVersion) {
|
||||
// nothing to do so far.
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected abstract List<Table> getTables();
|
||||
|
||||
public Cursor query(final String table, final String[] projection, final String selection,
|
||||
final String[] selectionArgs, final String orderBy) {
|
||||
return mHelper.getReadableDatabase()
|
||||
.query(table, projection, selection, selectionArgs, null /* groupBy */,
|
||||
null /* having */, orderBy);
|
||||
}
|
||||
|
||||
public long insert(final String table, final ContentValues values) {
|
||||
return mHelper.getWritableDatabase().insert(table, null /* nullColumnHack */, values);
|
||||
}
|
||||
|
||||
public int delete(final String table, final String selection, final String[] selectionArgs) {
|
||||
return mHelper.getWritableDatabase().delete(table, selection, selectionArgs);
|
||||
}
|
||||
|
||||
public int update(final String table, final ContentValues values, final String selection,
|
||||
final String[] selectionArgs) {
|
||||
return mHelper.getWritableDatabase().update(table, values, selection, selectionArgs);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
mHelper.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.db;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Abstraction of SQLite database row.
|
||||
*/
|
||||
public final class Entity<E> {
|
||||
|
||||
private final List<Field> mFields;
|
||||
private final Map<String, Field> mFieldMap;
|
||||
|
||||
private Entity(final Builder<E> builder) {
|
||||
mFields = builder.mFields;
|
||||
mFieldMap = builder.mFieldMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns SQL statement to create this entity/row, such that
|
||||
* "(_id INTEGER PRIMARY KEY AUTOINCREMENT, column2_name column2_type, ...)".
|
||||
*/
|
||||
public String createEntitySql() {
|
||||
final StringBuilder sb = new StringBuilder("(");
|
||||
for (final Field field : mFields) {
|
||||
if (field.pos > 0) sb.append(", ");
|
||||
sb.append(field.name).append(" ").append(field.sqLiteType);
|
||||
if (field.name.equals(BaseColumns._ID)) {
|
||||
sb.append(" PRIMARY KEY AUTOINCREMENT");
|
||||
}
|
||||
}
|
||||
return sb.append(")").toString();
|
||||
}
|
||||
|
||||
public Field getField(final String fieldName) {
|
||||
return mFieldMap.get(fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Entity} builder.
|
||||
*/
|
||||
public static final class Builder<E> {
|
||||
private final List<Field> mFields = new ArrayList<>();
|
||||
private final Map<String, Field> mFieldMap = new HashMap<>();
|
||||
private int mPos = 0;
|
||||
|
||||
public Builder() {
|
||||
addFieldInternal(BaseColumns._ID, Cursor.FIELD_TYPE_INTEGER);
|
||||
}
|
||||
|
||||
public Builder<E> addField(@NonNull final String name, final int fieldType) {
|
||||
addFieldInternal(name, fieldType);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Entity<E> build() {
|
||||
return new Entity<>(this);
|
||||
}
|
||||
|
||||
private void addFieldInternal(final String name, final int fieldType) {
|
||||
if (mFieldMap.containsKey(name)) {
|
||||
throw new IllegalArgumentException("Field " + name + " already exists at "
|
||||
+ mFieldMap.get(name).pos);
|
||||
}
|
||||
final Field field = Field.newInstance(mPos++, name, fieldType);
|
||||
mFields.add(field);
|
||||
mFieldMap.put(field.name, field);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.db;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
|
||||
/**
|
||||
* Abstraction of SQLite database column.
|
||||
*/
|
||||
public abstract class Field {
|
||||
|
||||
/** Field position in a row. */
|
||||
final int pos;
|
||||
|
||||
/** Column name of this field. */
|
||||
final String name;
|
||||
|
||||
/** Field type of SQLite. */
|
||||
final String sqLiteType;
|
||||
|
||||
public static Field newInstance(final int pos, final String name, final int fieldType) {
|
||||
switch (fieldType) {
|
||||
case Cursor.FIELD_TYPE_INTEGER:
|
||||
return new IntegerField(pos, name);
|
||||
case Cursor.FIELD_TYPE_STRING:
|
||||
return new StringField(pos, name);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown field type: " + fieldType);
|
||||
}
|
||||
}
|
||||
|
||||
private Field(final int pos, final String name, final int fieldType) {
|
||||
this.pos = pos;
|
||||
this.name = name;
|
||||
this.sqLiteType = toSqLiteType(fieldType);
|
||||
}
|
||||
|
||||
public long getLong(final Cursor cursor) {
|
||||
throw buildException(Cursor.FIELD_TYPE_INTEGER);
|
||||
}
|
||||
|
||||
public String getString(final Cursor cursor) {
|
||||
throw buildException(Cursor.FIELD_TYPE_STRING);
|
||||
}
|
||||
|
||||
public void putLong(final ContentValues values, final long value) {
|
||||
throw buildException(Cursor.FIELD_TYPE_INTEGER);
|
||||
}
|
||||
|
||||
public void putString(final ContentValues values, final String value) {
|
||||
throw buildException(Cursor.FIELD_TYPE_STRING);
|
||||
}
|
||||
|
||||
private UnsupportedOperationException buildException(final int expectedFieldType) {
|
||||
return new UnsupportedOperationException("Illegal type: " + name + " is " + sqLiteType
|
||||
+ ", expected " + toSqLiteType(expectedFieldType));
|
||||
}
|
||||
|
||||
private static String toSqLiteType(final int fieldType) {
|
||||
switch (fieldType) {
|
||||
case Cursor.FIELD_TYPE_NULL:
|
||||
return "NULL";
|
||||
case Cursor.FIELD_TYPE_INTEGER:
|
||||
return "INTEGER";
|
||||
case Cursor.FIELD_TYPE_FLOAT:
|
||||
return "REAL";
|
||||
case Cursor.FIELD_TYPE_STRING:
|
||||
return "TEXT";
|
||||
case Cursor.FIELD_TYPE_BLOB:
|
||||
return "BLOB";
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown field type: " + fieldType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction of INTEGER type field.
|
||||
*/
|
||||
private static final class IntegerField extends Field {
|
||||
|
||||
IntegerField(final int pos, final String name) {
|
||||
super(pos, name, Cursor.FIELD_TYPE_INTEGER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(final Cursor cursor) {
|
||||
return cursor.getLong(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putLong(final ContentValues values, final long value) {
|
||||
values.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction of STRING type field.
|
||||
*/
|
||||
private static final class StringField extends Field {
|
||||
|
||||
StringField(final int pos, final String name) {
|
||||
super(pos, name, Cursor.FIELD_TYPE_STRING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(final Cursor cursor) {
|
||||
return cursor.getString(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putString(final ContentValues values, final String value) {
|
||||
values.put(name, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.db;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Abstraction of SQLite database table.
|
||||
*/
|
||||
public abstract class Table<E> {
|
||||
|
||||
public final String mName;
|
||||
private final Entity<E> mEntity;
|
||||
|
||||
protected Table(final String name, final Entity<E> entity) {
|
||||
mName = name;
|
||||
mEntity = entity;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public abstract ContentValues buildContentValues(final E entity);
|
||||
|
||||
public abstract Stream<E> buildStream(final Cursor cursor);
|
||||
|
||||
/**
|
||||
* Returns SQL statement to create this table, such that
|
||||
* "CREATE TABLE IF NOT EXISTS table_name \
|
||||
* (_id INTEGER PRIMARY KEY AUTOINCREMENT, column2_name column2_type, ...)"
|
||||
*/
|
||||
@NonNull
|
||||
public String createTableSql() {
|
||||
return "CREATE TABLE IF NOT EXISTS " + mName + " " + mEntity.createEntitySql();
|
||||
}
|
||||
|
||||
protected Field getField(final String fieldName) {
|
||||
return mEntity.getField(fieldName);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.ime;
|
||||
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.HIDE_SOFT_INPUT;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_CREATE;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_DESTROY;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_FINISH_INPUT;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_FINISH_INPUT_VIEW;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_START_INPUT;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.ON_START_INPUT_VIEW;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.SHOW_SOFT_INPUT;
|
||||
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.inputmethodservice.cts.DeviceEvent;
|
||||
import android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventTypeParam;
|
||||
import android.inputmethodservice.cts.ime.ImeCommandReceiver.ImeCommandCallbacks;
|
||||
|
||||
import android.os.ResultReceiver;
|
||||
import android.util.Log;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public abstract class CtsBaseInputMethod extends InputMethodService implements ImeCommandCallbacks {
|
||||
|
||||
protected static final boolean DEBUG = false;
|
||||
|
||||
private final ImeCommandReceiver<CtsBaseInputMethod> mImeCommandReceiver =
|
||||
new ImeCommandReceiver<>();
|
||||
private String mLogTag;
|
||||
|
||||
private class CtsInputMethodImpl extends InputMethodImpl {
|
||||
@Override
|
||||
public void showSoftInput(int flags, ResultReceiver resultReceiver) {
|
||||
sendEvent(DeviceEvent.builder().setType(SHOW_SOFT_INPUT));
|
||||
if (DEBUG) {
|
||||
Log.d(mLogTag, "showSoftInput called");
|
||||
}
|
||||
super.showSoftInput(flags, resultReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
|
||||
sendEvent(DeviceEvent.builder().setType(HIDE_SOFT_INPUT));
|
||||
if (DEBUG) {
|
||||
Log.d(mLogTag, "hideSoftInput called");
|
||||
}
|
||||
super.hideSoftInput(flags, resultReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
mLogTag = getClass().getSimpleName();
|
||||
if (DEBUG) {
|
||||
Log.d(mLogTag, "onCreate:");
|
||||
}
|
||||
sendEvent(DeviceEvent.builder().setType(ON_CREATE));
|
||||
|
||||
super.onCreate();
|
||||
|
||||
mImeCommandReceiver.register(this /* ime */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartInput(EditorInfo editorInfo, boolean restarting) {
|
||||
if (DEBUG) {
|
||||
Log.d(mLogTag, "onStartInput:"
|
||||
+ " editorInfo=" + editorInfo
|
||||
+ " restarting=" + restarting);
|
||||
}
|
||||
|
||||
sendEvent(DeviceEvent.builder()
|
||||
.setType(ON_START_INPUT)
|
||||
.with(DeviceEventTypeParam.ON_START_INPUT_RESTARTING, restarting));
|
||||
super.onStartInput(editorInfo, restarting);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
|
||||
if (DEBUG) {
|
||||
Log.d(mLogTag, "onStartInputView:"
|
||||
+ " editorInfo=" + editorInfo
|
||||
+ " restarting=" + restarting);
|
||||
}
|
||||
|
||||
sendEvent(DeviceEvent.builder().setType(ON_START_INPUT_VIEW));
|
||||
|
||||
super.onStartInputView(editorInfo, restarting);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInputView(boolean finishingInput) {
|
||||
if (DEBUG) {
|
||||
Log.d(mLogTag, "onFinishInputView: finishingInput=" + finishingInput);
|
||||
}
|
||||
sendEvent(DeviceEvent.builder().setType(ON_FINISH_INPUT_VIEW));
|
||||
|
||||
super.onFinishInputView(finishingInput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInput() {
|
||||
if (DEBUG) {
|
||||
Log.d(mLogTag, "onFinishInput:");
|
||||
}
|
||||
sendEvent(DeviceEvent.builder().setType(ON_FINISH_INPUT));
|
||||
|
||||
super.onFinishInput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (DEBUG) {
|
||||
Log.d(mLogTag, "onDestroy:");
|
||||
}
|
||||
sendEvent(DeviceEvent.builder().setType(ON_DESTROY));
|
||||
|
||||
super.onDestroy();
|
||||
|
||||
unregisterReceiver(mImeCommandReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractInputMethodImpl onCreateInputMethodInterface() {
|
||||
final CtsInputMethodImpl inputMethod = new CtsInputMethodImpl();
|
||||
if (DEBUG) {
|
||||
Log.d(mLogTag, "onCreateInputMethodInterface");
|
||||
}
|
||||
|
||||
return inputMethod;
|
||||
}
|
||||
|
||||
//
|
||||
// Implementations of {@link ImeCommandCallbacks}.
|
||||
//
|
||||
|
||||
@Override
|
||||
public void commandCommitText(final CharSequence text, final int newCursorPosition) {
|
||||
executeOnInputConnection(ic -> {
|
||||
// TODO: Log the return value of {@link InputConnection#commitText(CharSequence,int)}.
|
||||
ic.commitText(text, newCursorPosition);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commandSwitchInputMethod(final String imeId) {
|
||||
switchInputMethod(imeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commandRequestHideSelf(final int flags) {
|
||||
requestHideSelf(flags);
|
||||
}
|
||||
|
||||
private void executeOnInputConnection(final Consumer<InputConnection> consumer) {
|
||||
final InputConnection ic = getCurrentInputConnection();
|
||||
// TODO: Check and log whether {@code ic} is null or equals to
|
||||
// {@link #getCurrentInputBindin().getConnection()}.
|
||||
if (ic != null) {
|
||||
consumer.accept(ic);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendEvent(final DeviceEvent.IntentBuilder intentBuilder) {
|
||||
intentBuilder.setSender(getClass().getName());
|
||||
sendBroadcast(intentBuilder.build());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (C) 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
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.ime;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.inputmethodservice.cts.common.ImeCommandConstants;
|
||||
import android.inputmethodservice.cts.ime.ImeCommandReceiver.ImeCommandCallbacks;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* {@link ImeCommandConstants#ACTION_IME_COMMAND} intent receiver.
|
||||
*/
|
||||
final class ImeCommandReceiver<T extends InputMethodService & ImeCommandCallbacks>
|
||||
extends BroadcastReceiver {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
interface ImeCommandCallbacks {
|
||||
/**
|
||||
* Callback method for {@link ImeCommandConstants#COMMAND_COMMIT_TEXT} intent.
|
||||
*
|
||||
* @param text text to be committed via {@link android.view.inputmethod.InputConnection}.
|
||||
* @param newCursorPosition new cursor position after commit.
|
||||
*/
|
||||
void commandCommitText(final CharSequence text, final int newCursorPosition);
|
||||
|
||||
/**
|
||||
* Callback method for {@link ImeCommandConstants#COMMAND_SWITCH_INPUT_METHOD} intent.
|
||||
*
|
||||
* @param imeId IME id to switch.
|
||||
*/
|
||||
void commandSwitchInputMethod(final String imeId);
|
||||
|
||||
/**
|
||||
* Callback method for {@link ImeCommandConstants#COMMAND_REQUEST_HIDE_SELF} intent.
|
||||
*/
|
||||
void commandRequestHideSelf(final int flags);
|
||||
}
|
||||
|
||||
private T mIme;
|
||||
|
||||
void register(final T ime) {
|
||||
mIme = ime;
|
||||
ime.registerReceiver(this, new IntentFilter(ImeCommandConstants.ACTION_IME_COMMAND));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
final String command = intent.getStringExtra(ImeCommandConstants.EXTRA_COMMAND);
|
||||
if (DEBUG) {
|
||||
Log.d(mIme.getClass().getSimpleName(), "onReceive: command=" + command);
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case ImeCommandConstants.COMMAND_COMMIT_TEXT: {
|
||||
final CharSequence text = getCharSequence1(intent);
|
||||
final int newCursorPosition = getInt1(intent);
|
||||
mIme.commandCommitText(text, newCursorPosition);
|
||||
return;
|
||||
}
|
||||
case ImeCommandConstants.COMMAND_SWITCH_INPUT_METHOD: {
|
||||
final String imeId = getString1(intent);
|
||||
mIme.commandSwitchInputMethod(imeId);
|
||||
return;
|
||||
}
|
||||
case ImeCommandConstants.COMMAND_REQUEST_HIDE_SELF: {
|
||||
final int flags = getInt1(intent);
|
||||
mIme.commandRequestHideSelf(flags);
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
throw new UnsupportedOperationException("Unknown IME command: " + command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static CharSequence getCharSequence1(final Intent intent) {
|
||||
return intent.getCharSequenceExtra(ImeCommandConstants.EXTRA_ARG_CHARSEQUENCE1);
|
||||
}
|
||||
|
||||
private static String getString1(final Intent intent) {
|
||||
return intent.getStringExtra(ImeCommandConstants.EXTRA_ARG_STRING1);
|
||||
}
|
||||
|
||||
private static int getInt1(final Intent intent) {
|
||||
if (intent.hasExtra(ImeCommandConstants.EXTRA_ARG_INT1)) {
|
||||
return intent.getIntExtra(ImeCommandConstants.EXTRA_ARG_INT1, 0);
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Needs " + ImeCommandConstants.EXTRA_ARG_INT1 + " in " + intent);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
# Copyright (C) 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# Don't include this package in any target
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
# When built, explicitly put it in the data partition.
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
|
||||
|
||||
LOCAL_DEX_PREOPT := false
|
||||
|
||||
LOCAL_PROGUARD_ENABLED := disabled
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
CtsInputMethodServiceCommon \
|
||||
CtsInputMethodServiceLib
|
||||
|
||||
# tag this module as a cts test artifact
|
||||
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
|
||||
|
||||
LOCAL_PACKAGE_NAME := CtsInputMethodServiceEventProvider
|
||||
|
||||
LOCAL_SDK_VERSION := current
|
||||
|
||||
include $(BUILD_PACKAGE)
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="android.inputmethodservice.cts.provider">
|
||||
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25" />
|
||||
|
||||
<application android:label="CtsInputMethodServiceEventProvider">
|
||||
<provider
|
||||
android:authorities="android.inputmethodservice.cts.provider"
|
||||
android:name="android.inputmethodservice.cts.provider.EventProvider"
|
||||
android:exported="true" />
|
||||
<receiver android:name="android.inputmethodservice.cts.receiver.EventReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.inputmethodservice.cts.action.IME_EVENT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.provider;
|
||||
|
||||
import static android.inputmethodservice.cts.common.EventProviderConstants.AUTHORITY;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.inputmethodservice.cts.common.EventProviderConstants.EventTableConstants;
|
||||
import android.inputmethodservice.cts.db.Database;
|
||||
import android.inputmethodservice.cts.db.Table;
|
||||
import android.inputmethodservice.cts.DeviceEvent;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* IME event content provider.
|
||||
*/
|
||||
public final class EventProvider extends ContentProvider {
|
||||
|
||||
private static final String TAG = EventProvider.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final String DB_NAME = "database";
|
||||
private static final int DB_VERSION = 1;
|
||||
|
||||
private UriHelper.Factory mUriFactory;
|
||||
private Database mDatabase;
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
mUriFactory = UriHelper.Factory.builder()
|
||||
.addUri(AUTHORITY, EventTableConstants.NAME, EventTableConstants.TYPE_DIR)
|
||||
.addUri(AUTHORITY, EventTableConstants.NAME + "/#", EventTableConstants.TYPE_ITEM)
|
||||
.build();
|
||||
mDatabase = new Database(getContext(), DB_NAME, DB_VERSION) {
|
||||
@Override
|
||||
@NonNull
|
||||
protected List<Table> getTables() {
|
||||
return Collections.singletonList(DeviceEvent.TABLE);
|
||||
}
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(@NonNull final Uri uri, @Nullable final String[] projection,
|
||||
final @Nullable String selection, @Nullable final String[] selectionArgs,
|
||||
@Nullable final String orderBy) {
|
||||
final UriHelper uriHelper = mUriFactory.newInstance(uri);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "query:"
|
||||
+ " uri=" + uri
|
||||
+ " projection=" + Arrays.toString(projection)
|
||||
+ " selection=" + uriHelper.buildSelection(selection)
|
||||
+ " selectionArgs=" + Arrays.toString(
|
||||
uriHelper.buildSelectionArgs(selectionArgs))
|
||||
+ " orderBy=" + orderBy);
|
||||
}
|
||||
final Cursor cursor = mDatabase.query(
|
||||
uriHelper.table, projection, uriHelper.buildSelection(selection),
|
||||
uriHelper.buildSelectionArgs(selectionArgs), orderBy);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, " query.count=" + cursor.getCount());
|
||||
}
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(@NonNull final Uri uri, @Nullable final ContentValues values) {
|
||||
final UriHelper uriHelper = mUriFactory.newInstance(uri);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "insert: uri=" + uri + " values={" + values + "}");
|
||||
}
|
||||
final long rowId = mDatabase.insert(uriHelper.table, values);
|
||||
final Uri insertedUri = ContentUris.withAppendedId(uri, rowId);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, " insert.uri=" + insertedUri);
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(insertedUri, null);
|
||||
return insertedUri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(@NonNull final Uri uri, @Nullable final String selection,
|
||||
@Nullable final String[] selectionArgs) {
|
||||
final UriHelper uriHelper = mUriFactory.newInstance(uri);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "delete:"
|
||||
+ " uri=" + uri
|
||||
+ " selection=" + uriHelper.buildSelection(selection)
|
||||
+ " selectionArgs=" + Arrays.toString(
|
||||
uriHelper.buildSelectionArgs(selectionArgs)));
|
||||
}
|
||||
final int count = mDatabase.delete(uriHelper.table, uriHelper.buildSelection(selection),
|
||||
uriHelper.buildSelectionArgs(selectionArgs));
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, " delete.count=" + count);
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(@NonNull final Uri uri, @Nullable final ContentValues values,
|
||||
final @Nullable String selection, @Nullable final String[] selectionArgs) {
|
||||
final UriHelper uriHelper = mUriFactory.newInstance(uri);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "update:"
|
||||
+ " uri=" + uri
|
||||
+ " values={" + values + "}"
|
||||
+ " selection=" + uriHelper.buildSelection(selection)
|
||||
+ " selectionArgs=" + Arrays.toString(
|
||||
uriHelper.buildSelectionArgs(selectionArgs)));
|
||||
}
|
||||
final int count = mDatabase.update(uriHelper.table, values,
|
||||
uriHelper.buildSelection(selection), uriHelper.buildSelectionArgs(selectionArgs));
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, " update.count=" + count);
|
||||
}
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getType(@NonNull final Uri uri) {
|
||||
return mUriFactory.getTypeOf(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
super.shutdown();
|
||||
mDatabase.close();
|
||||
mDatabase = null;
|
||||
mUriFactory = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.provider;
|
||||
|
||||
import android.content.UriMatcher;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Content URI helper.
|
||||
*
|
||||
* Helper object to parse content URI passed to content provider. A helper object is instantiated
|
||||
* via {@link Factory#newInstance(Uri)}, and a {@link Factory} object should be instantiated using
|
||||
* {@link FactoryBuilder}.
|
||||
*
|
||||
* A content URI is assumed to have a format "content://authority/table[/id]?" where table is a
|
||||
* SQLite table name in content provider and id is a primary key.
|
||||
*/
|
||||
final class UriHelper {
|
||||
|
||||
static final class Factory {
|
||||
private final UriMatcher mUriMatcher;
|
||||
private final SparseArray<String> mUriTypeMap;
|
||||
|
||||
public static FactoryBuilder builder() {
|
||||
return new FactoryBuilder();
|
||||
}
|
||||
|
||||
private Factory(final FactoryBuilder builder) {
|
||||
mUriMatcher = builder.mUriMatcher;
|
||||
mUriTypeMap = builder.mUriTypeMap;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UriHelper newInstance(final Uri uri) {
|
||||
if (mUriMatcher.match(uri) == UriMatcher.NO_MATCH) {
|
||||
throw new IllegalArgumentException("Unknown URI: " + uri);
|
||||
}
|
||||
return new UriHelper(uri);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
String getTypeOf(final Uri uri) {
|
||||
return mUriTypeMap.get(mUriMatcher.match(uri), null);
|
||||
}
|
||||
}
|
||||
|
||||
static final class FactoryBuilder {
|
||||
private final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||
private final SparseArray<String> mUriTypeMap = new SparseArray<>();
|
||||
private int mMatcherCode;
|
||||
|
||||
private FactoryBuilder() {
|
||||
mMatcherCode = 0;
|
||||
}
|
||||
|
||||
FactoryBuilder addUri(final String authority, final String path, final String type) {
|
||||
if (TextUtils.isEmpty(authority)) {
|
||||
throw new IllegalArgumentException("Authority must not be empty");
|
||||
}
|
||||
if (TextUtils.isEmpty(path)) {
|
||||
throw new IllegalArgumentException("Path must not be empty");
|
||||
}
|
||||
final int matcherCode = mMatcherCode++;
|
||||
mUriMatcher.addURI(authority, path, matcherCode);
|
||||
mUriTypeMap.append(matcherCode, type);
|
||||
return this;
|
||||
}
|
||||
|
||||
Factory build() {
|
||||
if (mMatcherCode == 0) {
|
||||
throw new IllegalStateException("No URI is defined");
|
||||
}
|
||||
return new Factory(this);
|
||||
}
|
||||
}
|
||||
|
||||
/** Name of SQLite table specified by content uri. */
|
||||
@NonNull
|
||||
final String table;
|
||||
|
||||
/** Primary id that is specified by content uri. Null if not. */
|
||||
@Nullable
|
||||
private final String mId;
|
||||
|
||||
private UriHelper(final Uri uri) {
|
||||
final List<String> segments = uri.getPathSegments();
|
||||
table = segments.get(0);
|
||||
mId = (segments.size() >= 2) ? segments.get(1) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes selection SQL text from content uri and {@code selection} specified.
|
||||
* When content uri has a primary key, it needs to be composed with a selection text specified
|
||||
* as content provider parameter.
|
||||
*
|
||||
* @param selection selection text specified as a parameter to content provider.
|
||||
* @return composed selection SQL text, null if no selection specified.
|
||||
*/
|
||||
@Nullable
|
||||
String buildSelection(@Nullable final String selection) {
|
||||
if (mId == null) {
|
||||
return selection;
|
||||
}
|
||||
// A primary key is specified by uri, so that selection should be at least "_id = ?".
|
||||
final StringBuilder sb = new StringBuilder().append(BaseColumns._ID).append(" = ?");
|
||||
if (selection != null) {
|
||||
// Selection is also specified as a parameter to content provider, so that it should be
|
||||
// appended with AND, such that "_id = ? AND (selection_text)".
|
||||
sb.append(" AND (").append(selection).append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes selection argument array from context uri and {@code selectionArgs} specified.
|
||||
* When content uri has a primary key, it needs to be provided in a final selection argument
|
||||
* array.
|
||||
*
|
||||
* @param selectionArgs selection argument array specified as a parameter to content provider.
|
||||
* @return composed selection argument array, null if selection argument is unnecessary.
|
||||
*/
|
||||
@Nullable
|
||||
String[] buildSelectionArgs(@Nullable final String[] selectionArgs) {
|
||||
if (mId == null) {
|
||||
return selectionArgs;
|
||||
}
|
||||
// A primary key is specified by uri but not as a parameter to content provider, the primary
|
||||
// key value should be the sole selection argument.
|
||||
if (selectionArgs == null || selectionArgs.length == 0) {
|
||||
return new String[]{ mId };
|
||||
}
|
||||
// Selection args are also specified as a parameter to content provider, the primary key
|
||||
// value should be prepended to those selection args.
|
||||
final String[] args = new String[selectionArgs.length + 1];
|
||||
System.arraycopy(selectionArgs, 0, args, 1, selectionArgs.length);
|
||||
args[0] = mId;
|
||||
return args;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.receiver;
|
||||
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_TIME;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.inputmethodservice.cts.DeviceEvent;
|
||||
import android.inputmethodservice.cts.common.EventProviderConstants.EventTableConstants;
|
||||
import android.net.Uri;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
public final class EventReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = EventReceiver.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final Uri CONTENT_URI = Uri.parse(EventTableConstants.CONTENT_URI);
|
||||
|
||||
@Override
|
||||
public void onReceive(final Context context, final Intent intent) {
|
||||
// Since {@code intent} which comes from host has no
|
||||
// {@link DeviceEventConstants#EXTRA_EVENT_TIME EXTRA_EVENT_TIME} extra, here we record the
|
||||
// time.
|
||||
if (!intent.hasExtra(EXTRA_EVENT_TIME)) {
|
||||
intent.putExtra(EXTRA_EVENT_TIME, SystemClock.uptimeMillis());
|
||||
}
|
||||
final DeviceEvent event = DeviceEvent.newEvent(intent);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onReceive: event=" + event);
|
||||
}
|
||||
final ContentValues values = DeviceEvent.buildContentValues(event);
|
||||
context.getContentResolver().insert(CONTENT_URI, values);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
# Copyright (C) 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.
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
# tag this module as a cts test artifact
|
||||
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
|
||||
|
||||
LOCAL_MODULE := CtsInputMethodServiceHostTestCases
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := \
|
||||
compatibility-host-util \
|
||||
cts-tradefed \
|
||||
tradefed
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
cts-inputmethodservice-common-host
|
||||
|
||||
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 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.
|
||||
-->
|
||||
|
||||
<configuration description="Config for CTS Input Method Service host test cases">
|
||||
<option name="config-descriptor:metadata" key="component" value="framework" />
|
||||
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
|
||||
<option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
|
||||
<option name="run-command" value="wm dismiss-keyguard" />
|
||||
</target_preparer>
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="cleanup-apks" value="true" />
|
||||
<option name="test-file-name" value="CtsInputMethodServiceEventProvider.apk" />
|
||||
<option name="test-file-name" value="CtsInputMethodServiceDeviceTests.apk" />
|
||||
</target_preparer>
|
||||
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
|
||||
<option name="jar" value="CtsInputMethodServiceHostTestCases.jar" />
|
||||
</test>
|
||||
</configuration>
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Copyright (C) 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.
|
||||
*/
|
||||
|
||||
package android.inputmethodservice.cts.hostside;
|
||||
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.ACTION_DEVICE_EVENT;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.DeviceEventType.TEST_START;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_SENDER;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.EXTRA_EVENT_TYPE;
|
||||
import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVER_COMPONENT;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import android.inputmethodservice.cts.common.EventProviderConstants.EventTableConstants;
|
||||
import android.inputmethodservice.cts.common.Ime1Constants;
|
||||
import android.inputmethodservice.cts.common.Ime2Constants;
|
||||
import android.inputmethodservice.cts.common.test.DeviceTestConstants;
|
||||
import android.inputmethodservice.cts.common.test.ShellCommandUtils;
|
||||
import android.inputmethodservice.cts.common.test.TestInfo;
|
||||
|
||||
import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
|
||||
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(DeviceJUnit4ClassRunner.class)
|
||||
public class InputMethodServiceLifecycleTest extends CompatibilityHostTestBase {
|
||||
|
||||
private String mDefaultImeId;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
// Skip whole tests when DUT has no android.software.input_methods feature.
|
||||
assumeTrue(Boolean.parseBoolean(shell(
|
||||
ShellCommandUtils.hasFeature(ShellCommandUtils.FEATURE_INPUT_METHODS))));
|
||||
mDefaultImeId = shell(ShellCommandUtils.getCurrentIme());
|
||||
cleanUpTestImes();
|
||||
shell(ShellCommandUtils.deleteContent(EventTableConstants.CONTENT_URI));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
shell(ShellCommandUtils.setCurrentIme(mDefaultImeId));
|
||||
cleanUpTestImes();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSwitchIme() throws Exception {
|
||||
final TestInfo testSwitchIme1ToIme2 = new TestInfo(DeviceTestConstants.PACKAGE,
|
||||
DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_SWITCH_IME1_TO_IME2);
|
||||
sendTestStartEvent(testSwitchIme1ToIme2);
|
||||
installPackage(Ime1Constants.APK, "-r");
|
||||
installPackage(Ime2Constants.APK, "-r");
|
||||
shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
|
||||
shell(ShellCommandUtils.enableIme(Ime2Constants.IME_ID));
|
||||
shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
|
||||
|
||||
assertTrue(runDeviceTestMethod(testSwitchIme1ToIme2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUninstallCurrentIme() throws Exception {
|
||||
installAndSetIme1();
|
||||
|
||||
final TestInfo testIme1IsNotCurrentIme = new TestInfo(DeviceTestConstants.PACKAGE,
|
||||
DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_IME1_IS_NOT_CURRENT_IME);
|
||||
sendTestStartEvent(testIme1IsNotCurrentIme);
|
||||
uninstallPackageIfExists(Ime1Constants.PACKAGE);
|
||||
assertTrue(runDeviceTestMethod(testIme1IsNotCurrentIme));
|
||||
assertEquals(shell(ShellCommandUtils.getCurrentIme()), mDefaultImeId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisableCurrentIme() throws Exception {
|
||||
installAndSetIme1();
|
||||
|
||||
final TestInfo testIme1IsNotCurrentIme = new TestInfo(DeviceTestConstants.PACKAGE,
|
||||
DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_IME1_IS_NOT_CURRENT_IME);
|
||||
sendTestStartEvent(testIme1IsNotCurrentIme);
|
||||
shell(ShellCommandUtils.disableIme(Ime1Constants.IME_ID));
|
||||
assertTrue(runDeviceTestMethod(testIme1IsNotCurrentIme));
|
||||
assertEquals(shell(ShellCommandUtils.getCurrentIme()), mDefaultImeId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchView_giveFocusShowIme() throws Exception {
|
||||
installAndSetIme1();
|
||||
|
||||
final TestInfo testGiveFocusShowIme1 = new TestInfo(DeviceTestConstants.PACKAGE,
|
||||
DeviceTestConstants.TEST_CLASS,
|
||||
DeviceTestConstants.TEST_SEARCH_VIEW_GIVE_FOCUS_SHOW_IME1);
|
||||
sendTestStartEvent(testGiveFocusShowIme1);
|
||||
assertTrue(runDeviceTestMethod(testGiveFocusShowIme1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchView_setQueryHideIme() throws Exception {
|
||||
installAndSetIme1();
|
||||
|
||||
final TestInfo testSetQueryHideIme1 = new TestInfo(DeviceTestConstants.PACKAGE,
|
||||
DeviceTestConstants.TEST_CLASS,
|
||||
DeviceTestConstants.TEST_SEARCH_VIEW_SET_QUERY_HIDE_IME1);
|
||||
sendTestStartEvent(testSetQueryHideIme1);
|
||||
assertTrue(runDeviceTestMethod(testSetQueryHideIme1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStartInputCalledOnce() throws Exception {
|
||||
installAndSetIme1();
|
||||
|
||||
final TestInfo testSetQueryHideIme1 = new TestInfo(DeviceTestConstants.PACKAGE,
|
||||
DeviceTestConstants.TEST_CLASS,
|
||||
DeviceTestConstants.TEST_ON_START_INPUT_CALLED_ONCE_IME1);
|
||||
sendTestStartEvent(testSetQueryHideIme1);
|
||||
assertTrue(runDeviceTestMethod(testSetQueryHideIme1));
|
||||
}
|
||||
|
||||
private void installAndSetIme1() throws Exception {
|
||||
final TestInfo testCreateIme1 = new TestInfo(DeviceTestConstants.PACKAGE,
|
||||
DeviceTestConstants.TEST_CLASS, DeviceTestConstants.TEST_CREATE_IME1);
|
||||
sendTestStartEvent(testCreateIme1);
|
||||
installPackage(Ime1Constants.APK, "-r");
|
||||
shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
|
||||
shell(ShellCommandUtils.setCurrentIme(Ime1Constants.IME_ID));
|
||||
assertTrue(runDeviceTestMethod(testCreateIme1));
|
||||
}
|
||||
|
||||
private void sendTestStartEvent(final TestInfo deviceTest) throws Exception {
|
||||
final String sender = deviceTest.getTestName();
|
||||
// {@link EventType#EXTRA_EVENT_TIME} will be recorded at device side.
|
||||
shell(ShellCommandUtils.broadcastIntent(
|
||||
ACTION_DEVICE_EVENT, RECEIVER_COMPONENT,
|
||||
"--es", EXTRA_EVENT_SENDER, sender,
|
||||
"--es", EXTRA_EVENT_TYPE, TEST_START.name()));
|
||||
}
|
||||
|
||||
private boolean runDeviceTestMethod(final TestInfo deviceTest) throws Exception {
|
||||
return runDeviceTests(deviceTest.testPackage, deviceTest.testClass, deviceTest.testMethod);
|
||||
}
|
||||
|
||||
private String shell(final String command) throws Exception {
|
||||
return getDevice().executeShellCommand(command).trim();
|
||||
}
|
||||
|
||||
private void cleanUpTestImes() throws Exception {
|
||||
uninstallPackageIfExists(Ime1Constants.PACKAGE);
|
||||
uninstallPackageIfExists(Ime2Constants.PACKAGE);
|
||||
}
|
||||
|
||||
private void uninstallPackageIfExists(final String packageName) throws Exception {
|
||||
if (isPackageInstalled(packageName)) {
|
||||
uninstallPackage(packageName);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue