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;
22b6e0cb97b7d8e4acf345efc8066f1587408b83e0Alan Viveretteimport android.os.UserHandle;
230cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.provider.Settings;
240cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.view.View;
250cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.view.View.AccessibilityDelegate;
260cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.view.accessibility.AccessibilityEvent;
270cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport android.view.accessibility.AccessibilityNodeInfo;
280cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
290cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvimport com.android.internal.R;
300cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
310cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv/**
320cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * Accessibility delegate that obscures speech for a view when the user has
330cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * not turned on the "speak passwords" preference and is not listening
340cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv * through headphones.
350cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv */
360cbbc5766fb7c56c49ec55eece905fa7764a8b2balanvclass ObscureSpeechDelegate extends AccessibilityDelegate {
370cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    /** Whether any client has announced the "headset" notification. */
380cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    static boolean sAnnouncedHeadset = false;
390cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
400cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    private final ContentResolver mContentResolver;
410cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    private final AudioManager mAudioManager;
420cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
430cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    public ObscureSpeechDelegate(Context context) {
440cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        mContentResolver = context.getContentResolver();
450cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
460cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    }
470cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
480cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    @Override
490cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    public void sendAccessibilityEvent(View host, int eventType) {
500cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        super.sendAccessibilityEvent(host, eventType);
510cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
520cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        // Play the "headset required" announcement the first time the user
530cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        // places accessibility focus on a key.
540cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        if ((eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED)
550cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv                && !sAnnouncedHeadset && shouldObscureSpeech()) {
560cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv            sAnnouncedHeadset = true;
570cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv            host.announceForAccessibility(host.getContext().getString(
580cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv                    R.string.keyboard_headset_required_to_hear_password));
590cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        }
600cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    }
610cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
620cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    @Override
630cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
640cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        super.onPopulateAccessibilityEvent(host, event);
650cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
660cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        if ((event.getEventType() != AccessibilityEvent.TYPE_ANNOUNCEMENT)
670cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv                && shouldObscureSpeech()) {
680cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv            event.getText().clear();
690cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv            event.setContentDescription(host.getContext().getString(
700cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv                    R.string.keyboard_password_character_no_headset));
710cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        }
720cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    }
730cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
740cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    @Override
750cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
760cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        super.onInitializeAccessibilityNodeInfo(host, info);
770cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
780cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        if (shouldObscureSpeech()) {
790cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv            final Context ctx = host.getContext();
800cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv            info.setText(null);
810cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv            info.setContentDescription(
820cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv                    ctx.getString(R.string.keyboard_password_character_no_headset));
830cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        }
840cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    }
850cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
860cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    @SuppressWarnings("deprecation")
870cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    private boolean shouldObscureSpeech() {
880cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        // The user can optionally force speaking passwords.
89b6e0cb97b7d8e4acf345efc8066f1587408b83e0Alan Viverette        if (Settings.Secure.getIntForUser(mContentResolver,
90b6e0cb97b7d8e4acf345efc8066f1587408b83e0Alan Viverette                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, 0, UserHandle.USER_CURRENT) != 0) {
910cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv            return false;
920cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        }
930cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
940cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        // Always speak if the user is listening through headphones.
950cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn()) {
960cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv            return false;
970cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        }
980cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv
990cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        // Don't speak since this key is used to type a password.
1000cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv        return true;
1010cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv    }
1020cbbc5766fb7c56c49ec55eece905fa7764a8b2balanv}