summaryrefslogtreecommitdiffstats
path: root/src/com/android/phone/common/HapticFeedback.java
blob: f19b8a602260058a42cfcbde6b27d1c8b9532c98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
 * Copyright (C) 2009 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.phone.common;

import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.os.SystemVibrator;
import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.System;
import android.util.Log;

/**
 * Handles the haptic feedback: a light buzz happening when the user
 * presses a soft key (UI button or capacitive key).  The haptic
 * feedback is controlled by:
 * - a system resource for the pattern
 *   The pattern used is tuned per device and stored in an internal
 *   resource (config_virtualKeyVibePattern.)
 * - a system setting HAPTIC_FEEDBACK_ENABLED.
 *   HAPTIC_FEEDBACK_ENABLED can be changed by the user using the
 *   system Settings activity. It must be rechecked each time the
 *   activity comes in the foreground (onResume).
 *
 * This class is not thread safe. It assumes it'll be called from the
 * UI thead.
 *
 * Typical usage:
 * --------------
 *   static private final boolean HAPTIC_ENABLED = true;
 *   private HapticFeedback mHaptic = new HapticFeedback();
 *
 *   protected void onCreate(Bundle icicle) {
 *     mHaptic.init((Context)this, HAPTIC_ENABLED);
 *   }
 *
 *   protected void onResume() {
 *     // Refresh the system setting.
 *     mHaptic.checkSystemSetting();
 *   }
 *
 *   public void foo() {
 *     mHaptic.vibrate();
 *   }
 *
 */

public class HapticFeedback {
    /** If no pattern was found, vibrate for a small amount of time. */
    private static final long DURATION = 10;  // millisec.
    /** Play the haptic pattern only once. */
    private static final int NO_REPEAT = -1;

    private static final String TAG = "HapticFeedback";
    private Context mContext;
    private long[] mHapticPattern;
    private Vibrator mVibrator;

    private boolean mEnabled;
    private Settings.System mSystemSettings;
    private ContentResolver mContentResolver;
    private boolean mSettingEnabled;

    /**
     * Initialize this instance using the app and system
     * configs. Since these don't change, init is typically called
     * once in 'onCreate'.
     * checkSettings is not called during init.
     * @param context To look up the resources and system settings.
     * @param enabled If false, vibrate will be a no-op regardless of
     * the system settings.
     */
    public void init(Context context, boolean enabled) {
        mEnabled = enabled;
        if (enabled) {
            // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this
            // vibrator object will be isolated from others.
            mVibrator = new SystemVibrator(context);
            mHapticPattern = new long[] {0, DURATION, 2 * DURATION, 3 * DURATION};
            mSystemSettings = new Settings.System();
            mContentResolver = context.getContentResolver();
        }
    }


    /**
     * Reload the system settings to check if the user enabled the
     * haptic feedback.
     */
    public void checkSystemSetting() {
        if (!mEnabled) {
            return;
        }
        try {
            int val = mSystemSettings.getInt(mContentResolver, System.HAPTIC_FEEDBACK_ENABLED, 0);
            mSettingEnabled = val != 0;
        } catch (Resources.NotFoundException nfe) {
            Log.e(TAG, "Could not retrieve system setting.", nfe);
            mSettingEnabled = false;
        }

    }


    /**
     * Generate the haptic feedback vibration. Only one thread can
     * request it. If the phone is already in a middle of an haptic
     * feedback sequence, the request is ignored.
     */
    public void vibrate() {
        if (!mEnabled || !mSettingEnabled) {
            return;
        }
        // System-wide configuration may return different styles of haptic feedback pattern.
        // - an array with one value implies "one-shot vibration"
        // - an array with multiple values implies "pattern vibration"
        // We need to switch methods to call depending on the difference.
        // See also PhoneWindowManager#performHapticFeedbackLw() for another example.
        if (mHapticPattern != null && mHapticPattern.length == 1) {
            mVibrator.vibrate(mHapticPattern[0]);
        } else {
            mVibrator.vibrate(mHapticPattern, NO_REPEAT);
        }
    }
}