ObscureSpeechDelegate.java revision 5ecd81154fa039961f65bb4e36d18ac555b0d1d6
10cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv/* 20cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * Copyright (C) 2013 The Android Open Source Project 30cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * 40cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * Licensed under the Apache License, Version 2.0 (the "License"); 50cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * you may not use this file except in compliance with the License. 60cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * You may obtain a copy of the License at 70cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * 80cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * http://www.apache.org/licenses/LICENSE-2.0 90cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * 100cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * Unless required by applicable law or agreed to in writing, software 110cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * distributed under the License is distributed on an "AS IS" BASIS, 120cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * See the License for the specific language governing permissions and 140cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * limitations under the License. 150cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv */ 160cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 175ecd81154fa039961f65bb4e36d18ac555b0d1d6Jim Millerpackage com.android.keyguard; 180cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 190cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.content.ContentResolver; 200cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.content.Context; 210cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.media.AudioManager; 220cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.provider.Settings; 230cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.view.View; 240cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.view.View.AccessibilityDelegate; 250cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.view.accessibility.AccessibilityEvent; 260cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.view.accessibility.AccessibilityNodeInfo; 270cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 280cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport com.android.internal.R; 290cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 300cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv/** 310cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * Accessibility delegate that obscures speech for a view when the user has 320cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * not turned on the "speak passwords" preference and is not listening 330cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * through headphones. 340cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv */ 350cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvclass ObscureSpeechDelegate extends AccessibilityDelegate { 360cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv /** Whether any client has announced the "headset" notification. */ 370cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv static boolean sAnnouncedHeadset = false; 380cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 390cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv private final ContentResolver mContentResolver; 400cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv private final AudioManager mAudioManager; 410cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 420cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv public ObscureSpeechDelegate(Context context) { 430cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv mContentResolver = context.getContentResolver(); 440cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 450cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv } 460cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 470cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv @Override 480cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv public void sendAccessibilityEvent(View host, int eventType) { 490cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv super.sendAccessibilityEvent(host, eventType); 500cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 510cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv // Play the "headset required" announcement the first time the user 520cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv // places accessibility focus on a key. 530cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv if ((eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) 540cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv && !sAnnouncedHeadset && shouldObscureSpeech()) { 550cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv sAnnouncedHeadset = true; 560cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv host.announceForAccessibility(host.getContext().getString( 570cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv R.string.keyboard_headset_required_to_hear_password)); 580cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv } 590cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv } 600cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 610cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv @Override 620cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { 630cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv super.onPopulateAccessibilityEvent(host, event); 640cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 650cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv if ((event.getEventType() != AccessibilityEvent.TYPE_ANNOUNCEMENT) 660cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv && shouldObscureSpeech()) { 670cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv event.getText().clear(); 680cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv event.setContentDescription(host.getContext().getString( 690cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv R.string.keyboard_password_character_no_headset)); 700cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv } 710cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv } 720cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 730cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv @Override 740cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 750cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv super.onInitializeAccessibilityNodeInfo(host, info); 760cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 770cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv if (shouldObscureSpeech()) { 780cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv final Context ctx = host.getContext(); 790cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv info.setText(null); 800cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv info.setContentDescription( 810cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv ctx.getString(R.string.keyboard_password_character_no_headset)); 820cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv } 830cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv } 840cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 850cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv @SuppressWarnings("deprecation") 860cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv private boolean shouldObscureSpeech() { 870cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv // The user can optionally force speaking passwords. 880cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv if (Settings.Secure.getInt(mContentResolver, 890cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0) { 900cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv return false; 910cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv } 920cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 930cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv // Always speak if the user is listening through headphones. 940cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn()) { 950cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv return false; 960cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv } 970cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv 980cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv // Don't speak since this key is used to type a password. 990cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv return true; 1000cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv } 1010cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv}