Snap for 10103804 from 3263e306750cd4f450799a37b5b3351a147901f1 to mainline-tzdata5-release
Change-Id: I6714cc6b0535c221c833ef6f832ab5340226da97
diff --git a/readme.md b/readme.md
index ad5745d..30902d7 100644
--- a/readme.md
+++ b/readme.md
@@ -43,3 +43,9 @@
```
adb shell cmd car_service inject-key 23
```
+
+To long click the controller center button, send down and up action seperately. For example:
+```
+adb shell cmd car_service inject-key 23 -a down && sleep 2 && adb shell cmd car_service inject-key 23 -a up
+```
+
diff --git a/src/com/android/car/rotary/Navigator.java b/src/com/android/car/rotary/Navigator.java
index a493a2f..c417958 100644
--- a/src/com/android/car/rotary/Navigator.java
+++ b/src/com/android/car/rotary/Navigator.java
@@ -559,11 +559,8 @@
}
boolean hasFocusableDescendant = false;
for (AccessibilityNodeInfo webView : webViews) {
- AccessibilityNodeInfo focusableDescendant = mTreeTraverser.depthFirstSearch(webView,
- Utils::canPerformFocus);
- if (focusableDescendant != null) {
+ if (webViewHasFocusableDescendants(webView)) {
hasFocusableDescendant = true;
- focusableDescendant.recycle();
break;
}
}
@@ -571,6 +568,20 @@
return hasFocusableDescendant;
}
+ private boolean webViewHasFocusableDescendants(@NonNull AccessibilityNodeInfo webView) {
+ AccessibilityNodeInfo focusableDescendant = mTreeTraverser.depthFirstSearch(webView,
+ Utils::canPerformFocus);
+ if (focusableDescendant == null) {
+ return false;
+ }
+ focusableDescendant.recycle();
+ return true;
+ }
+
+ private boolean isWebViewWithFocusableDescendants(@NonNull AccessibilityNodeInfo node) {
+ return Utils.isWebView(node) && webViewHasFocusableDescendants(node);
+ }
+
/**
* Adds all the {@code windows} in the given {@code direction} of the given {@code source}
* window to the given list if the {@code source} window is not an overlay. If it's an overlay
@@ -815,7 +826,7 @@
if (Utils.isFocusArea(node) || Utils.isFocusParkingView(node)) {
return bounds;
}
- if (Utils.canTakeFocus(node) || containsWebViewWithFocusableDescendants(node)) {
+ if (Utils.canTakeFocus(node) || isWebViewWithFocusableDescendants(node)) {
return Utils.getBoundsInScreen(node);
}
for (int i = 0; i < node.getChildCount(); i++) {
diff --git a/src/com/android/car/rotary/RotaryService.java b/src/com/android/car/rotary/RotaryService.java
index ec05b50..0ef9572 100644
--- a/src/com/android/car/rotary/RotaryService.java
+++ b/src/com/android/car/rotary/RotaryService.java
@@ -100,7 +100,6 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
-import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;
@@ -615,13 +614,20 @@
Context.MODE_PRIVATE);
mUserManager = getSystemService(UserManager.class);
+ mInputManager = getSystemService(InputManager.class);
+ mInputMethodManager = getSystemService(InputMethodManager.class);
+ if (mInputMethodManager == null) {
+ throw new IllegalStateException("Failed to get InputMethodManager");
+ }
+
mRotaryInputMethod = res.getString(R.string.rotary_input_method);
mDefaultTouchInputMethod = res.getString(R.string.default_touch_input_method);
- mTouchInputMethod = mPrefs.getString(TOUCH_INPUT_METHOD_PREFIX + mUserManager.getUserName(),
- mDefaultTouchInputMethod);
- if (mRotaryInputMethod != null
- && mRotaryInputMethod.equals(getCurrentIme())
- && isInstalledIme(mTouchInputMethod)) {
+ validateImeConfiguration(mDefaultTouchInputMethod);
+ mTouchInputMethod = mPrefs.getString(TOUCH_INPUT_METHOD_PREFIX
+ + mUserManager.getUserName(), mDefaultTouchInputMethod);
+ validateImeConfiguration(mTouchInputMethod);
+
+ if (mRotaryInputMethod != null && mRotaryInputMethod.equals(getCurrentIme())) {
// Switch from the rotary IME to the touch IME in case Android defaults to the rotary
// IME.
// TODO(b/169423887): Figure out how to configure the default IME through Android
@@ -670,6 +676,21 @@
}
/**
+ * Ensure that the IME configuration passed as argument is also available in
+ * {@link InputMethodManager}.
+ *
+ * @throws IllegalStateException if the ime configuration passed as argument is not available
+ * in {@link InputMethodManager}
+ */
+ private void validateImeConfiguration(String imeConfiguration) {
+ if (!Utils.isInstalledIme(imeConfiguration, mInputMethodManager)) {
+ throw new IllegalStateException(String.format("%s is not installed (run "
+ + "`dumpsys input_method` to list all available input methods)",
+ imeConfiguration));
+ }
+ }
+
+ /**
* {@inheritDoc}
* <p>
* We need to access WindowManager in onCreate() and
@@ -716,11 +737,6 @@
updateServiceInfo();
- mInputManager = getSystemService(InputManager.class);
- mInputMethodManager = getSystemService(InputMethodManager.class);
- if (mInputMethodManager == null) {
- L.w("Failed to get InputMethodManager");
- }
// Add an overlay to capture touch events.
addTouchOverlay();
@@ -2701,7 +2717,7 @@
/** Switches to the rotary IME or the touch IME if needed. */
private void updateIme() {
String newIme = mInRotaryMode ? mRotaryInputMethod : mTouchInputMethod;
- if (mInRotaryMode && !isInstalledIme(newIme)) {
+ if (mInRotaryMode && !Utils.isInstalledIme(newIme, mInputMethodManager)) {
L.w("Rotary IME doesn't exist: " + newIme);
return;
}
@@ -2725,9 +2741,11 @@
if (mContentResolver == null) {
return;
}
+ String oldIme = getCurrentIme();
+ validateImeConfiguration(newIme);
boolean result =
Settings.Secure.putString(mContentResolver, DEFAULT_INPUT_METHOD, newIme);
- L.successOrFailure("Switching to IME: " + newIme, result);
+ L.successOrFailure("Switching IME from " + oldIme + " to " + newIme, result);
}
/**
@@ -2880,25 +2898,6 @@
mWindowCache.setNodeCopier(nodeCopier);
}
- /** Checks if the {@code componentName} is an installed input method. */
- private boolean isInstalledIme(@Nullable String componentName) {
- if (TextUtils.isEmpty(componentName) || mInputMethodManager == null) {
- return false;
- }
- // Use getInputMethodList() to get the installed input methods. Don't do that by fetching
- // ENABLED_INPUT_METHODS and DISABLED_SYSTEM_INPUT_METHODS from the secure setting,
- // because RotaryIME may not be included in any of them (b/229144904).
- ComponentName component = ComponentName.unflattenFromString(componentName);
- List<InputMethodInfo> imeList = mInputMethodManager.getInputMethodList();
- for (InputMethodInfo ime : imeList) {
- ComponentName imeComponent = ime.getComponent();
- if (component.equals(imeComponent)) {
- return true;
- }
- }
- return false;
- }
-
@VisibleForTesting
AccessibilityNodeInfo getFocusedNode() {
return mFocusedNode;
@@ -2981,5 +2980,4 @@
RotaryProtos.RotaryService.WINDOW_CACHE);
dumpOutputStream.flush();
}
-
}
diff --git a/src/com/android/car/rotary/Utils.java b/src/com/android/car/rotary/Utils.java
index 89bfa58..27f7028 100644
--- a/src/com/android/car/rotary/Utils.java
+++ b/src/com/android/car/rotary/Utils.java
@@ -30,11 +30,15 @@
import static com.android.car.ui.utils.RotaryConstants.ROTARY_VERTICALLY_SCROLLABLE;
import static com.android.car.ui.utils.RotaryConstants.TOP_BOUND_OFFSET_FOR_NUDGE;
+import android.content.ComponentName;
import android.graphics.Rect;
import android.os.Bundle;
+import android.text.TextUtils;
import android.view.SurfaceView;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView;
import androidx.annotation.NonNull;
@@ -427,4 +431,24 @@
}
return null;
}
+
+ /** Checks if the {@code componentName} is an installed input method. */
+ static boolean isInstalledIme(@Nullable String componentName,
+ @NonNull InputMethodManager imm) {
+ if (TextUtils.isEmpty(componentName)) {
+ return false;
+ }
+ // Use getInputMethodList() to get the installed input methods. Don't do that by fetching
+ // ENABLED_INPUT_METHODS and DISABLED_SYSTEM_INPUT_METHODS from the secure setting,
+ // because RotaryIME may not be included in any of them (b/229144904).
+ ComponentName component = ComponentName.unflattenFromString(componentName);
+ List<InputMethodInfo> imeList = imm.getInputMethodList();
+ for (InputMethodInfo ime : imeList) {
+ ComponentName imeComponent = ime.getComponent();
+ if (component.equals(imeComponent)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/tests/unit/src/com/android/car/rotary/UtilsTest.java b/tests/unit/src/com/android/car/rotary/UtilsTest.java
index e688350..91bdef3 100644
--- a/tests/unit/src/com/android/car/rotary/UtilsTest.java
+++ b/tests/unit/src/com/android/car/rotary/UtilsTest.java
@@ -22,15 +22,24 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.view.accessibility.AccessibilityNodeInfo;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
-@RunWith(AndroidJUnit4.class)
-public class UtilsTest {
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class UtilsTest {
+
+ @Mock
+ private InputMethodManager mMockedInputMethodManager;
@Test
public void refreshNode_nodeIsNull_returnsNull() {
@@ -68,4 +77,21 @@
verify(input).recycle();
}
+
+ @Test
+ public void testIsInstalledIme_invalidImeConfigs() {
+ assertThat(Utils.isInstalledIme(null, mMockedInputMethodManager)).isFalse();
+ assertThat(Utils.isInstalledIme("blah/someIme", mMockedInputMethodManager)).isFalse();
+ }
+
+ @Test
+ public void testIsInstalledIme_validImeConfig() {
+ InputMethodInfo methodInfo = mock(InputMethodInfo.class);
+ when(methodInfo.getComponent()).thenReturn(
+ ComponentName.unflattenFromString("blah/someIme"));
+ List<InputMethodInfo> availableInputMethods = Collections.singletonList(methodInfo);
+ when(mMockedInputMethodManager.getInputMethodList()).thenReturn(availableInputMethods);
+
+ assertThat(Utils.isInstalledIme("blah/someIme", mMockedInputMethodManager)).isTrue();
+ }
}