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}