Add supportsDismissingSelfWindow attribute of IME
When a virtual keyboard is shown on some configurations (e.g. Phone),
the System UI may change the back navigation button to a different UI
element in order to dismiss the virtual keyboard. Such UI modification
is unnecessary when the virtual keyboard has a dismissing button on
its own window. This new attribute hints the System UI that the
virtual keyboard may have a UI element to dismiss itself. This will be
also useful for Tablet System UI which may not show a navigation bar
when a virtual keyboard is shown.
Bug: 34133139
Test: Add unit test InputMethodInfoTest
Change-Id: I0f6b130a7df57557e40b52a7b7ac00be965a17c3
diff --git a/api/current.txt b/api/current.txt
index bc41d65..6bbfe71 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1234,6 +1234,7 @@
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportsAssist = 16844016; // 0x10104f0
+ field public static final int supportsDismissingWindow = 16844104; // 0x1010548
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
diff --git a/api/system-current.txt b/api/system-current.txt
index 24f9aad..12881f8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1347,6 +1347,7 @@
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportsAssist = 16844016; // 0x10104f0
+ field public static final int supportsDismissingWindow = 16844104; // 0x1010548
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
diff --git a/api/test-current.txt b/api/test-current.txt
index 4c277e4..07330e5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1234,6 +1234,7 @@
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportsAssist = 16844016; // 0x10104f0
+ field public static final int supportsDismissingWindow = 16844104; // 0x1010548
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 5c8e6dc..b6da1d8 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -57,6 +57,7 @@
* @attr ref android.R.styleable#InputMethod_settingsActivity
* @attr ref android.R.styleable#InputMethod_isDefault
* @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod
+ * @attr ref android.R.styleable#InputMethod_supportsDismissingWindow
*/
public final class InputMethodInfo implements Parcelable {
static final String TAG = "InputMethodInfo";
@@ -104,6 +105,11 @@
private final boolean mSupportsSwitchingToNextInputMethod;
/**
+ * The flag whether this IME supports ways to dismiss its window (e.g. dismiss button.)
+ */
+ private final boolean mSupportsDismissingWindow;
+
+ /**
* Constructor.
*
* @param context The Context in which we are parsing the input method.
@@ -132,6 +138,7 @@
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
boolean isAuxIme = true;
boolean supportsSwitchingToNextInputMethod = false; // false as default
+ boolean supportsDismissingWindow = false; // false as default
mForceDefault = false;
PackageManager pm = context.getPackageManager();
@@ -171,6 +178,8 @@
supportsSwitchingToNextInputMethod = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_supportsSwitchingToNextInputMethod,
false);
+ supportsDismissingWindow = sa.getBoolean(
+ com.android.internal.R.styleable.InputMethod_supportsDismissingWindow, false);
sa.recycle();
final int depth = parser.getDepth();
@@ -242,6 +251,7 @@
mIsDefaultResId = isDefaultResId;
mIsAuxIme = isAuxIme;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+ mSupportsDismissingWindow = supportsDismissingWindow;
}
InputMethodInfo(Parcel source) {
@@ -250,6 +260,7 @@
mIsDefaultResId = source.readInt();
mIsAuxIme = source.readInt() == 1;
mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
+ mSupportsDismissingWindow = source.readInt() == 1;
mService = ResolveInfo.CREATOR.createFromParcel(source);
mSubtypes = new InputMethodSubtypeArray(source);
mForceDefault = false;
@@ -260,8 +271,10 @@
*/
public InputMethodInfo(String packageName, String className,
CharSequence label, String settingsActivity) {
- this(buildDummyResolveInfo(packageName, className, label), false, settingsActivity, null,
- 0, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */);
+ this(buildDummyResolveInfo(packageName, className, label), false /* isAuxIme */,
+ settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
+ false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
+ true /* supportsDismissingWindow */);
}
/**
@@ -271,17 +284,18 @@
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
boolean forceDefault) {
- this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId,
- forceDefault, true /* supportsSwitchingToNextInputMethod */);
+ this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
+ true /* supportsSwitchingToNextInputMethod */,
+ true /* supportsDismissingWindow */);
}
/**
* Temporary API for creating a built-in input method for test.
* @hide
*/
- public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
- String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
- boolean forceDefault, boolean supportsSwitchingToNextInputMethod) {
+ public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
+ List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
+ boolean supportsSwitchingToNextInputMethod, boolean supportsDismissingWindow) {
final ServiceInfo si = ri.serviceInfo;
mService = ri;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -291,6 +305,7 @@
mSubtypes = new InputMethodSubtypeArray(subtypes);
mForceDefault = forceDefault;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+ mSupportsDismissingWindow = supportsDismissingWindow;
}
private static ResolveInfo buildDummyResolveInfo(String packageName, String className,
@@ -431,7 +446,8 @@
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName
- + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod);
+ + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
+ + " mSupportsDismissingWindow=" + mSupportsDismissingWindow);
pw.println(prefix + "mIsDefaultResId=0x"
+ Integer.toHexString(mIsDefaultResId));
pw.println(prefix + "Service:");
@@ -484,6 +500,14 @@
}
/**
+ * @return true if this input method supports ways to dismiss its window.
+ * @hide
+ */
+ public boolean supportsDismissingWindow() {
+ return mSupportsDismissingWindow;
+ }
+
+ /**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
@@ -496,6 +520,7 @@
dest.writeInt(mIsDefaultResId);
dest.writeInt(mIsAuxIme ? 1 : 0);
dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
+ dest.writeInt(mSupportsDismissingWindow ? 1 : 0);
mService.writeToParcel(dest, flags);
mSubtypes.writeToParcel(dest);
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 5458e7c..dd33718 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3178,6 +3178,18 @@
and subtype in order to provide the consistent user experience in switching
between IMEs and subtypes. -->
<attr name="supportsSwitchingToNextInputMethod" format="boolean" />
+ <!-- Set to true if this input method supports ways to dismiss the windows assigned to
+ the input method (e.g. a dismiss button rendered by the input method itself). The
+ System UI may optimize the UI by not showing system-level dismiss button if this
+ value is true.
+ <p> Must be a boolean value, either "true" or "false". The default value is "false".
+ <p> This may also be a reference to a resource (in the form "@[package:]type:name")
+ or theme attribute (in the form "?[package:]type:name") containing a value of this
+ type.
+ <p> A UI element that dismisses the input method window should report
+ {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_DISMISS} action, so
+ that accessibility services can handle it accordingly. -->
+ <attr name="supportsDismissingWindow" format="boolean" />
</declare-styleable>
<!-- This is the subtype of InputMethod. Subtype can describe locales (e.g. en_US, fr_FR...)
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 73cba89..064d31e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2784,6 +2784,7 @@
<public name="focusedByDefault" />
<public name="appCategory" />
<public name="autoSizeMaxTextSize" />
+ <public name="supportsDismissingWindow" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/tests/coretests/res/xml/ime_meta.xml b/core/tests/coretests/res/xml/ime_meta.xml
new file mode 100644
index 0000000..a975718
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta.xml
@@ -0,0 +1,27 @@
+<?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:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+>
+ <subtype
+ android:label="subtype1"
+ android:imeSubtypeLocale="en_US"
+ android:imeSubtypeMode="keyboard" />
+</input-method>
diff --git a/core/tests/coretests/res/xml/ime_meta_dismiss.xml b/core/tests/coretests/res/xml/ime_meta_dismiss.xml
new file mode 100644
index 0000000..59f8ecc
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta_dismiss.xml
@@ -0,0 +1,28 @@
+<?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:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+ android:supportsDismissingWindow="true"
+>
+ <subtype
+ android:label="subtype1"
+ android:imeSubtypeLocale="en_US"
+ android:imeSubtypeMode="keyboard" />
+</input-method>
diff --git a/core/tests/coretests/res/xml/ime_meta_sw_next.xml b/core/tests/coretests/res/xml/ime_meta_sw_next.xml
new file mode 100644
index 0000000..2e2ee33
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta_sw_next.xml
@@ -0,0 +1,28 @@
+<?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:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+ android:supportsSwitchingToNextInputMethod="true"
+>
+ <subtype
+ android:label="subtype1"
+ android:imeSubtypeLocale="en_US"
+ android:imeSubtypeMode="keyboard" />
+</input-method>
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
new file mode 100644
index 0000000..23dc80f
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.view.inputmethod;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.annotation.XmlRes;
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class InputMethodInfoTest {
+
+ @Test
+ public void testEqualsAndHashCode() throws Exception {
+ final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta);
+ final InputMethodInfo clone = cloneViaParcel(imi);
+
+ assertThat(clone.equals(imi), is(true));
+ assertThat(clone.hashCode(), equalTo(imi.hashCode()));
+ }
+
+ @Test
+ public void testBooleanAttributes_DefaultValues() throws Exception {
+ final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta);
+
+ assertThat(imi.supportsSwitchingToNextInputMethod(), is(false));
+ assertThat(imi.supportsDismissingWindow(), is(false));
+
+ final InputMethodInfo clone = cloneViaParcel(imi);
+
+ assertThat(clone.supportsSwitchingToNextInputMethod(), is(false));
+ assertThat(clone.supportsDismissingWindow(), is(false));
+ }
+
+ @Test
+ public void testSupportsSwitchingToNextInputMethod() throws Exception {
+ final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_sw_next);
+
+ assertThat(imi.supportsSwitchingToNextInputMethod(), is(true));
+
+ final InputMethodInfo clone = cloneViaParcel(imi);
+
+ assertThat(clone.supportsSwitchingToNextInputMethod(), is(true));
+ }
+
+ @Test
+ public void testSupportsDismissingWindow() throws Exception {
+ final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_dismiss);
+
+ assertThat(imi.supportsDismissingWindow(), is(true));
+
+ final InputMethodInfo clone = cloneViaParcel(imi);
+
+ assertThat(clone.supportsDismissingWindow(), is(true));
+ }
+
+ private InputMethodInfo buildInputMethodForTest(final @XmlRes int metaDataRes)
+ throws Exception {
+ final Context context = InstrumentationRegistry.getContext();
+ final ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.applicationInfo = context.getApplicationInfo();
+ serviceInfo.packageName = context.getPackageName();
+ serviceInfo.name = "DummyImeForTest";
+ serviceInfo.metaData = new Bundle();
+ serviceInfo.metaData.putInt(InputMethod.SERVICE_META_DATA, metaDataRes);
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.serviceInfo = serviceInfo;
+ return new InputMethodInfo(context, resolveInfo, null /* additionalSubtypesMap */);
+ }
+
+ private InputMethodInfo cloneViaParcel(final InputMethodInfo original) {
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ original.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return InputMethodInfo.CREATOR.createFromParcel(parcel);
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index ba5206a..34c34d7 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -74,7 +74,8 @@
}
final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
- DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
+ DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod,
+ false /* supportsDismissingWindow */);
if (subtypes == null) {
items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE));
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
index f33362f..4df199c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
@@ -159,14 +159,14 @@
for (String pkg : defaultImes) {
final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
final InputMethodInfo inputMethodInfo = new InputMethodInfo(
- ri, false, null, null, 0, true, true);
+ ri, false, null, null, 0, true, true, false);
inputMethods.add(inputMethodInfo);
addInstalledApp(ri);
}
for (String pkg : otherImes) {
final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
final InputMethodInfo inputMethodInfo = new InputMethodInfo(
- ri, false, null, null, 0, false, true);
+ ri, false, null, null, 0, false, true, false);
inputMethods.add(inputMethodInfo);
addInstalledApp(ri);
}