diff options
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); |