summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/ViewRootImpl.java1
-rw-r--r--core/res/res/layout/keyguard_pin_view.xml2
-rw-r--r--core/res/res/layout/keyguard_sim_pin_view.xml4
-rw-r--r--core/res/res/layout/keyguard_sim_puk_view.xml4
-rw-r--r--core/res/res/values/styles.xml3
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java2
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/LiftToActivateListener.java71
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java53
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/ObscureSpeechDelegate.java101
-rw-r--r--services/java/com/android/server/accessibility/AccessibilityManagerService.java21
10 files changed, 214 insertions, 48 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a3360bc6b8d9..b6016e93be53 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2338,6 +2338,7 @@ public final class ViewRootImpl implements ViewParent,
mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds);
}
bounds.offset(-mAttachInfo.mWindowLeft, -mAttachInfo.mWindowTop);
+ bounds.intersect(0, 0, mAttachInfo.mViewRootImpl.mWidth, mAttachInfo.mViewRootImpl.mHeight);
drawable.setBounds(bounds);
drawable.draw(canvas);
}
diff --git a/core/res/res/layout/keyguard_pin_view.xml b/core/res/res/layout/keyguard_pin_view.xml
index 29e69f30d576..d6cfe2acd324 100644
--- a/core/res/res/layout/keyguard_pin_view.xml
+++ b/core/res/res/layout/keyguard_pin_view.xml
@@ -193,10 +193,10 @@
<ImageButton
android:id="@+id/key_enter"
style="@style/Widget.Button.NumPadKey"
- android:gravity="center"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
+ android:paddingRight="30dp"
android:src="@drawable/sym_keyboard_return_holo"
android:contentDescription="@string/keyboardview_keycode_enter"
/>
diff --git a/core/res/res/layout/keyguard_sim_pin_view.xml b/core/res/res/layout/keyguard_sim_pin_view.xml
index aed73e5ab7a6..36e1b150cff7 100644
--- a/core/res/res/layout/keyguard_sim_pin_view.xml
+++ b/core/res/res/layout/keyguard_sim_pin_view.xml
@@ -65,6 +65,7 @@
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:background="?android:attr/selectableItemBackground"
+ android:contentDescription="@string/keyboardview_keycode_delete"
/>
</LinearLayout>
<View
@@ -197,11 +198,12 @@
<ImageButton
android:id="@+id/key_enter"
style="@style/Widget.Button.NumPadKey"
- android:gravity="center"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
+ android:paddingRight="30dp"
android:src="@drawable/sym_keyboard_return_holo"
+ android:contentDescription="@string/keyboardview_keycode_enter"
/>
</LinearLayout>
diff --git a/core/res/res/layout/keyguard_sim_puk_view.xml b/core/res/res/layout/keyguard_sim_puk_view.xml
index 7eec2cae4908..e846a7bb9b9b 100644
--- a/core/res/res/layout/keyguard_sim_puk_view.xml
+++ b/core/res/res/layout/keyguard_sim_puk_view.xml
@@ -66,6 +66,7 @@
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:background="?android:attr/selectableItemBackground"
+ android:contentDescription="@string/keyboardview_keycode_delete"
/>
</LinearLayout>
<View
@@ -198,11 +199,12 @@
<ImageButton
android:id="@+id/key_enter"
style="@style/Widget.Button.NumPadKey"
- android:gravity="center"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
+ android:paddingRight="30dp"
android:src="@drawable/sym_keyboard_return_holo"
+ android:contentDescription="@string/keyboardview_keycode_enter"
/>
</LinearLayout>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 180f864bb3c4..f489786937de 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -2482,13 +2482,14 @@ please see styles_device_defaults.xml.
<style name="Widget.Button.NumPadKey"
parent="@android:style/Widget.Button">
<item name="android:singleLine">true</item>
- <item name="android:padding">6dip</item>
<item name="android:gravity">left|center_vertical</item>
<item name="android:background">?android:attr/selectableItemBackground</item>
<item name="android:textSize">34dp</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">#ffffff</item>
+ <item name="android:paddingBottom">10dp</item>
+ <item name="android:paddingLeft">20dp</item>
</style>
<style name="TextAppearance.NumPadKey"
parent="@android:style/TextAppearance">
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java
index cea3fb09cc82..fa803525a1e9 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardPINView.java
@@ -70,7 +70,7 @@ public class KeyguardPINView extends KeyguardAbsKeyInputView
}
}
});
- ok.setOnHoverListener(new NumPadKey.LiftToActivateListener(getContext()));
+ ok.setOnHoverListener(new LiftToActivateListener(getContext()));
}
// The delete button is of the PIN keyboard itself in some (e.g. tablet) layouts,
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/LiftToActivateListener.java b/policy/src/com/android/internal/policy/impl/keyguard/LiftToActivateListener.java
new file mode 100644
index 000000000000..818108c67f22
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/LiftToActivateListener.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+
+/**
+ * Hover listener that implements lift-to-activate interaction for
+ * accessibility. May be added to multiple views.
+ */
+class LiftToActivateListener implements View.OnHoverListener {
+ /** Manager used to query accessibility enabled state. */
+ private final AccessibilityManager mAccessibilityManager;
+
+ private boolean mCachedClickableState;
+
+ public LiftToActivateListener(Context context) {
+ mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ }
+
+ @Override
+ public boolean onHover(View v, MotionEvent event) {
+ // When touch exploration is turned on, lifting a finger while
+ // inside the view bounds should perform a click action.
+ if (mAccessibilityManager.isEnabled()
+ && mAccessibilityManager.isTouchExplorationEnabled()) {
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ // Lift-to-type temporarily disables double-tap
+ // activation by setting the view as not clickable.
+ mCachedClickableState = v.isClickable();
+ v.setClickable(false);
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ final int x = (int) event.getX();
+ final int y = (int) event.getY();
+ if ((x > v.getPaddingLeft()) && (y > v.getPaddingTop())
+ && (x < v.getWidth() - v.getPaddingRight())
+ && (y < v.getHeight() - v.getPaddingBottom())) {
+ v.performClick();
+ }
+ v.setClickable(mCachedClickableState);
+ break;
+ }
+ }
+
+ // Pass the event to View.onHoverEvent() to handle accessibility.
+ v.onHoverEvent(event);
+
+ // Consume the event so it doesn't fall through to other views.
+ return true;
+ }
+} \ No newline at end of file
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java
index 7f51a84458bb..a0038bc92fda 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/NumPadKey.java
@@ -22,9 +22,7 @@ import android.text.SpannableStringBuilder;
import android.text.style.TextAppearanceSpan;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.accessibility.AccessibilityManager;
import android.widget.Button;
import android.widget.TextView;
@@ -76,6 +74,7 @@ public class NumPadKey extends Button {
setOnClickListener(mListener);
setOnHoverListener(new LiftToActivateListener(context));
+ setAccessibilityDelegate(new ObscureSpeechDelegate(context));
mEnableHaptics = new LockPatternUtils(context).isTactileFeedbackEnabled();
@@ -90,6 +89,7 @@ public class NumPadKey extends Button {
final String extra = sKlondike[mDigit];
final int extraLen = extra.length();
if (extraLen > 0) {
+ builder.append(" ");
builder.append(extra);
builder.setSpan(
new TextAppearanceSpan(context, R.style.TextAppearance_NumPadKey_Klondike),
@@ -100,6 +100,14 @@ public class NumPadKey extends Button {
setText(builder);
}
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ // Reset the "announced headset" flag when detached.
+ ObscureSpeechDelegate.sAnnouncedHeadset = false;
+ }
+
public void setTextView(TextView tv) {
mTextView = tv;
}
@@ -117,45 +125,4 @@ public class NumPadKey extends Button {
| HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
}
-
- /**
- * Hover listener that implements lift-to-activate interaction for
- * accessibility. May be added to multiple views.
- */
- static class LiftToActivateListener implements View.OnHoverListener {
- /** Manager used to query accessibility enabled state. */
- private final AccessibilityManager mAccessibilityManager;
-
- public LiftToActivateListener(Context context) {
- mAccessibilityManager = (AccessibilityManager) context.getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- }
-
- @Override
- public boolean onHover(View v, MotionEvent event) {
- // When touch exploration is turned on, lifting a finger while
- // inside the view bounds should perform a click action.
- if (mAccessibilityManager.isEnabled()
- && mAccessibilityManager.isTouchExplorationEnabled()) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_HOVER_ENTER:
- // Lift-to-type temporarily disables double-tap
- // activation.
- v.setClickable(false);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- if ((x > v.getPaddingLeft()) && (y > v.getPaddingTop())
- && (x < v.getWidth() - v.getPaddingRight())
- && (y < v.getHeight() - v.getPaddingBottom())) {
- v.performClick();
- }
- v.setClickable(true);
- break;
- }
- }
- return false;
- }
- }
}
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/ObscureSpeechDelegate.java b/policy/src/com/android/internal/policy/impl/keyguard/ObscureSpeechDelegate.java
new file mode 100644
index 000000000000..af043abbf11c
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/ObscureSpeechDelegate.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.media.AudioManager;
+import android.provider.Settings;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.internal.R;
+
+/**
+ * Accessibility delegate that obscures speech for a view when the user has
+ * not turned on the "speak passwords" preference and is not listening
+ * through headphones.
+ */
+class ObscureSpeechDelegate extends AccessibilityDelegate {
+ /** Whether any client has announced the "headset" notification. */
+ static boolean sAnnouncedHeadset = false;
+
+ private final ContentResolver mContentResolver;
+ private final AudioManager mAudioManager;
+
+ public ObscureSpeechDelegate(Context context) {
+ mContentResolver = context.getContentResolver();
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ }
+
+ @Override
+ public void sendAccessibilityEvent(View host, int eventType) {
+ super.sendAccessibilityEvent(host, eventType);
+
+ // Play the "headset required" announcement the first time the user
+ // places accessibility focus on a key.
+ if ((eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED)
+ && !sAnnouncedHeadset && shouldObscureSpeech()) {
+ sAnnouncedHeadset = true;
+ host.announceForAccessibility(host.getContext().getString(
+ R.string.keyboard_headset_required_to_hear_password));
+ }
+ }
+
+ @Override
+ public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ super.onPopulateAccessibilityEvent(host, event);
+
+ if ((event.getEventType() != AccessibilityEvent.TYPE_ANNOUNCEMENT)
+ && shouldObscureSpeech()) {
+ event.getText().clear();
+ event.setContentDescription(host.getContext().getString(
+ R.string.keyboard_password_character_no_headset));
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+
+ if (shouldObscureSpeech()) {
+ final Context ctx = host.getContext();
+ info.setText(null);
+ info.setContentDescription(
+ ctx.getString(R.string.keyboard_password_character_no_headset));
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private boolean shouldObscureSpeech() {
+ // The user can optionally force speaking passwords.
+ if (Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0) {
+ return false;
+ }
+
+ // Always speak if the user is listening through headphones.
+ if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn()) {
+ return false;
+ }
+
+ // Don't speak since this key is used to type a password.
+ return true;
+ }
+} \ No newline at end of file
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6b277c7887e2..4953f48bb76d 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -40,7 +40,9 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.hardware.input.InputManager;
import android.net.Uri;
import android.os.Binder;
@@ -62,6 +64,7 @@ import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.Display;
import android.view.IWindow;
import android.view.IWindowManager;
import android.view.InputDevice;
@@ -137,6 +140,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList =
new ArrayList<AccessibilityServiceInfo>();
+ private final Rect mTempRect = new Rect();
+
+ private final Point mTempPoint = new Point();
+
+ private final Display mDefaultDisplay;
+
private final PackageManager mPackageManager;
private final IWindowManager mWindowManagerService;
@@ -194,6 +203,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mWindowManagerService = (IWindowManager) ServiceManager.getService(Context.WINDOW_SERVICE);
mSecurityPolicy = new SecurityPolicy();
mMainHandler = new MainHandler(mContext.getMainLooper());
+ //TODO: (multi-display) We need to support multiple displays.
+ DisplayManager displayManager = (DisplayManager)
+ mContext.getSystemService(Context.DISPLAY_SERVICE);
+ mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
registerBroadcastReceivers();
new AccessibilityContentObserver(mMainHandler).register(
context.getContentResolver());
@@ -582,6 +595,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
* @param outBounds The output to which to write the focus bounds.
* @return Whether accessibility focus was found and the bounds are populated.
*/
+ // TODO: (multi-display) Make sure this works for multiple displays.
boolean getAccessibilityFocusBoundsInActiveWindow(Rect outBounds) {
// Instead of keeping track of accessibility focus events per
// window to be able to find the focus in the active window,
@@ -603,6 +617,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return false;
}
focus.getBoundsInScreen(outBounds);
+ // Clip to the window rectangle.
+ Rect windowBounds = mTempRect;
+ getActiveWindowBounds(windowBounds);
+ outBounds.intersect(windowBounds);
+ // Clip to the screen rectangle.
+ mDefaultDisplay.getRealSize(mTempPoint);
+ outBounds.intersect(0, 0, mTempPoint.x, mTempPoint.y);
return true;
} finally {
client.removeConnection(connectionId);