1564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard/*
2564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard * Copyright (C) 2012 The Android Open Source Project
3564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard *
48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License.
68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at
7564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard *
88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
9564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard *
10564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard * Unless required by applicable law or agreed to in writing, software
118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and
148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License.
15564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard */
16564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard
17564496bad6207f02e7a653872213bc5954e84ce4Jean Chalardpackage com.android.inputmethod.latin;
18564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard
19564496bad6207f02e7a653872213bc5954e84ce4Jean Chalardimport android.content.Context;
20564496bad6207f02e7a653872213bc5954e84ce4Jean Chalardimport android.media.AudioManager;
21b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaokaimport android.os.Vibrator;
22564496bad6207f02e7a653872213bc5954e84ce4Jean Chalardimport android.view.HapticFeedbackConstants;
23544c3c29527927239a6484efc30bc22f9cc4dad1Jean Chalardimport android.view.View;
24564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard
25564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard/**
26564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard * This class gathers audio feedback and haptic feedback functions.
27564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard *
28564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard * It offers a consistent and simple interface that allows LatinIME to forget about the
29564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard * complexity of settings and the like.
30564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard */
31a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class AudioAndHapticFeedbackManager {
32b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    private AudioManager mAudioManager;
33b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    private Vibrator mVibrator;
348e360c68f1861a70fdb91652334efa513e25fcd2Tadashi G. Takaoka
358e360c68f1861a70fdb91652334efa513e25fcd2Tadashi G. Takaoka    private SettingsValues mSettingsValues;
362651b17fc4fb4457deb5078114b26d3a1ec66799Jean Chalard    private boolean mSoundOn;
37564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard
38b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    private static final AudioAndHapticFeedbackManager sInstance =
39b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka            new AudioAndHapticFeedbackManager();
40b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka
41b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    public static AudioAndHapticFeedbackManager getInstance() {
42b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        return sInstance;
43b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    }
44b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka
45b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    private AudioAndHapticFeedbackManager() {
46b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        // Intentional empty constructor for singleton.
47b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    }
48b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka
49b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    public static void init(final Context context) {
50b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        sInstance.initInternal(context);
51b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    }
52b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka
53b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    private void initInternal(final Context context) {
54b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
55b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
56564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard    }
57564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard
58544c3c29527927239a6484efc30bc22f9cc4dad1Jean Chalard    public void hapticAndAudioFeedback(final int primaryCode,
59544c3c29527927239a6484efc30bc22f9cc4dad1Jean Chalard            final View viewToPerformHapticFeedbackOn) {
60b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        vibrateInternal(viewToPerformHapticFeedbackOn);
61564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard        playKeyClick(primaryCode);
62564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard    }
63564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard
64b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    public boolean hasVibrator() {
65b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        return mVibrator != null && mVibrator.hasVibrator();
66b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    }
67b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka
68b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    public void vibrate(final long milliseconds) {
69b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        if (mVibrator == null) {
70b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka            return;
71b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        }
72b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        mVibrator.vibrate(milliseconds);
73b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    }
74b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka
75bdbb22bb3315bc22e1641fb8ecd150fc2de4a19dJean Chalard    private boolean reevaluateIfSoundIsOn() {
768e360c68f1861a70fdb91652334efa513e25fcd2Tadashi G. Takaoka        if (mSettingsValues == null || !mSettingsValues.mSoundOn || mAudioManager == null) {
77bdbb22bb3315bc22e1641fb8ecd150fc2de4a19dJean Chalard            return false;
78564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard        }
79b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL;
80564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard    }
81564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard
82b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    private void playKeyClick(final int primaryCode) {
83793791cdf0d54a55a73f931ffe3c62590c881f1eJean Chalard        // if mAudioManager is null, we can't play a sound anyway, so return
84b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        if (mAudioManager == null) {
85b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka            return;
86b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        }
87bdbb22bb3315bc22e1641fb8ecd150fc2de4a19dJean Chalard        if (mSoundOn) {
88564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard            final int sound;
89564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard            switch (primaryCode) {
90240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_DELETE:
91564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard                sound = AudioManager.FX_KEYPRESS_DELETE;
92564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard                break;
93240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_ENTER:
94564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard                sound = AudioManager.FX_KEYPRESS_RETURN;
95564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard                break;
96240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka            case Constants.CODE_SPACE:
97564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard                sound = AudioManager.FX_KEYPRESS_SPACEBAR;
98564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard                break;
99564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard            default:
100564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard                sound = AudioManager.FX_KEYPRESS_STANDARD;
101564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard                break;
102564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard            }
103d3b0ecec22cda883150851dced32c1eda2910a66Tadashi G. Takaoka            mAudioManager.playSoundEffect(sound, mSettingsValues.mKeypressSoundVolume);
104564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard        }
105564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard    }
106564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard
107b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka    private void vibrateInternal(final View viewToPerformHapticFeedbackOn) {
108564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard        if (!mSettingsValues.mVibrateOn) {
109564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard            return;
110564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard        }
111564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard        if (mSettingsValues.mKeypressVibrationDuration < 0) {
112564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard            // Go ahead with the system default
113544c3c29527927239a6484efc30bc22f9cc4dad1Jean Chalard            if (viewToPerformHapticFeedbackOn != null) {
114544c3c29527927239a6484efc30bc22f9cc4dad1Jean Chalard                viewToPerformHapticFeedbackOn.performHapticFeedback(
115564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard                        HapticFeedbackConstants.KEYBOARD_TAP,
116564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard                        HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
117564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard            }
118b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka            return;
119564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard        }
120b17b88742528d09eaa36adb3dae5348b99c234e0Tadashi G. Takaoka        vibrate(mSettingsValues.mKeypressVibrationDuration);
121564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard    }
122564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard
1238e360c68f1861a70fdb91652334efa513e25fcd2Tadashi G. Takaoka    public void onSettingsChanged(final SettingsValues settingsValues) {
1248e360c68f1861a70fdb91652334efa513e25fcd2Tadashi G. Takaoka        mSettingsValues = settingsValues;
1258e360c68f1861a70fdb91652334efa513e25fcd2Tadashi G. Takaoka        mSoundOn = reevaluateIfSoundIsOn();
1268e360c68f1861a70fdb91652334efa513e25fcd2Tadashi G. Takaoka    }
1278e360c68f1861a70fdb91652334efa513e25fcd2Tadashi G. Takaoka
12821af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard    public void onRingerModeChanged() {
12921af2f40c59de3ea5ec183aa278406bf28d5e3bdJean Chalard        mSoundOn = reevaluateIfSoundIsOn();
130564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard    }
131564496bad6207f02e7a653872213bc5954e84ce4Jean Chalard}
132