1. 程式人生 > >Android 觸控提示音

Android 觸控提示音

近期任務,涉及Android觸控提示音。
首先,定位原始碼目標。很顯然的,在原生的設定的聲音功能頁裡面就包含了觸控音的開關。
那麼我們找到對應的java程式碼,SoundSettings.java

package com.android.settings;
import java.util.List;

public class SoundSettings extends SettingsPreferenceFragment implements
        Preference.OnPreferenceChangeListener {

可以看到 這是個PreferenceFragment的子類。(這裡的SettingsPreferenceFragment是繼承PreferenceFragment)。那麼找到它對應的xml檔案。

 @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ContentResolver resolver = getContentResolver();
        int activePhoneType = TelephonyManager.getDefault().getCurrentPhoneType();

        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

        addPreferencesFromResource(R.xml.sound_settings);

常識性的,它在onCreate裡面,找到addPreferencesFromResource,然後檢視sound_settings的xml檔案。
這裡我通過查詢中文string資原始檔,標示了xml檔案的一些item的title。

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
        android:title="@string/sound_settings"
        android:key="sound_settings"
        xmlns:settings
="http://schemas.android.com/apk/res/com.android.settings">
<!-- 音量--> <com.android.settings.RingerVolumePreference android:key="ring_volume" android:title="@string/all_volume_title" android:dialogTitle="@string/all_volume_title" android:persistent="false" android:streamType="ring" /> <!-- 音樂效果--> <Preference android:key="musicfx" android:title="@string/musicfx_title"> <intent android:targetPackage="com.android.musicfx" android:targetClass="com.android.musicfx.ControlPanelPicker" /> </Preference> <!-- 來電鈴聲和振動--> <PreferenceCategory android:key="category_calls_and_notification" android:title="@string/sound_category_call_ringtone_vibrate_title"/> <!-- Do not nest these, or removals in code will break 手機鈴聲 --> <com.android.settings.DefaultRingtonePreference android:key="ringtone" android:title="@string/ringtone_title" android:dialogTitle="@string/ringtone_title" android:persistent="false" android:ringtoneType="ringtone" /> <!-- 響鈴時振動--> <CheckBoxPreference android:key="vibrate_when_ringing" android:title="@string/vibrate_when_ringing_title" android:persistent="false" /> <!-- 系統--> <PreferenceCategory android:title="@string/sound_category_system_title"/> <!-- Do not nest these, or removals in code will break --> <!-- 預設通知提示音--> <com.android.settings.DefaultRingtonePreference android:key="notification_sound" android:title="@string/notification_sound_title" android:dialogTitle="@string/notification_sound_dialog_title" android:persistent="false" android:ringtoneType="notification" /> <!-- 撥號鍵盤觸控音效--> <CheckBoxPreference android:key="dtmf_tone" android:title="@string/dtmf_tone_enable_title" android:defaultValue="true" /> <!-- 觸控提示音--> <CheckBoxPreference android:key="sound_effects" android:title="@string/sound_effects_enable_title" android:defaultValue="true" /> <!-- 鎖屏提示音--> <CheckBoxPreference android:key="lock_sounds" android:title="@string/lock_sounds_enable_title" android:defaultValue="true" /> <!-- 觸控時振動--> <CheckBoxPreference android:key="haptic_feedback" android:title="@string/haptic_feedback_enable_title" android:defaultValue="true" /> <!-- 緊急提示音--> <ListPreference android:key="emergency_tone" android:title="@string/emergency_tone_title" android:entries="@array/emergency_tone_entries" android:entryValues="@array/emergency_tone_values" />

OK,接著我們在SoundSettings裡面搜尋關鍵字: sound_effects

private static final String KEY_SOUND_EFFECTS = "sound_effects";

接著搜尋:KEY_SOUND_EFFECTS

        // 觸控提示音相關
        mSoundEffects = (CheckBoxPreference) findPreference(KEY_SOUND_EFFECTS);
        mSoundEffects.setPersistent(false);
        mSoundEffects.setChecked(Settings.System.getInt(resolver,
        Settings.System.SOUND_EFFECTS_ENABLED, 1) != 0);
    @Override
    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
        if (preference == mVibrateWhenRinging) {
            Settings.System.putInt(getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING,
                    mVibrateWhenRinging.isChecked() ? 1 : 0);
        } else if (preference == mDtmfTone) {
            Settings.System.putInt(getContentResolver(), Settings.System.DTMF_TONE_WHEN_DIALING,
            mDtmfTone.isChecked() ? 1 : 0);

        } else if (preference == mSoundEffects) {
            if (mSoundEffects.isChecked()) {
                mAudioManager.loadSoundEffects();
            } else {
                mAudioManager.unloadSoundEffects();
            }
            Settings.System.putInt(getContentResolver(), Settings.System.SOUND_EFFECTS_ENABLED,
            mSoundEffects.isChecked() ? 1 : 0);

        }

這裡我們很容易的看清楚它的例項和點選監聽,都是通過修改系統配置來進行操作的,那麼這玩意到底在哪裡整呢?
首先,觸控提示音,是對所有的view的點選時間都會有觸發效果,那麼我們看看View.java的類。
View.java有將近2萬行,想研究的徹底很明顯是不現實,或者說,不輕鬆的。那麼我們搜素SOUND_EFFECTS_ENABLED 這個關鍵字

    /**
     * View flag indicating whether this view should have sound effects enabled
     * for events such as clicking and touching.
     */
    public static final int SOUND_EFFECTS_ENABLED = 0x08000000;

看註釋,意思是是否啟動聲音效果。

    /**
     * Set whether this view should have sound effects enabled for events such as
     * clicking and touching.
     *
     * <p>You may wish to disable sound effects for a view if you already play sounds,
     * for instance, a dial key that plays dtmf tones.
     *
     * @param soundEffectsEnabled whether sound effects are enabled for this view.
     * @see #isSoundEffectsEnabled()
     * @see #playSoundEffect(int)
     * @attr ref android.R.styleable#View_soundEffectsEnabled
     */
    public void setSoundEffectsEnabled(boolean soundEffectsEnabled) {
        setFlags(soundEffectsEnabled ? SOUND_EFFECTS_ENABLED: 0, SOUND_EFFECTS_ENABLED);
    }

    /**
     * @return whether this view should have sound effects enabled for events such as
     *     clicking and touching.
     *
     * @see #setSoundEffectsEnabled(boolean)
     * @see #playSoundEffect(int)
     * @attr ref android.R.styleable#View_soundEffectsEnabled
     */
    @ViewDebug.ExportedProperty
    public boolean isSoundEffectsEnabled() {
        return SOUND_EFFECTS_ENABLED == (mViewFlags & SOUND_EFFECTS_ENABLED);
    }

看到這2個方法我就有底了。很明顯,我們要檢視isSoundEffectsEnabled的呼叫關係。

    /**
     * Play a sound effect for this view.
     *
     * <p>The framework will play sound effects for some built in actions, such as
     * clicking, but you may wish to play these effects in your widget,
     * for instance, for internal navigation.
     *
     * <p>The sound effect will only be played if sound effects are enabled by the user, and
     * {@link #isSoundEffectsEnabled()} is true.
     *
     * @param soundConstant One of the constants defined in {@link SoundEffectConstants}
     */
    public void playSoundEffect(int soundConstant) {
        if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {
            return;
        }
        mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant);
    }

接著我們看誰呼叫了它,找到在AudioServeice.java中

    /** @see AudioManager#playSoundEffect(int) */
    public void playSoundEffect(int effectType) {
        playSoundEffectVolume(effectType, -1.0f);
    }

    /** @see AudioManager#playSoundEffect(int, float) */
    public void playSoundEffectVolume(int effectType, float volume) {
        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_QUEUE,
        effectType, (int) (volume * 1000), null, 0);
    }

寫到這裡,就是完了。接下來的需要,也就是對觸控音的資原始檔的修改,它的位置在framework/base/data/sounds/effects 資料夾下。(同時這個資料夾的ogg下面也有一個相同的檔案,這個我還不清楚)Effect_Tick.ogg。
對應的是在Android system/media/audio/ui Effect_Tick.ogg的檔案。
想要修改它的話,可以在編譯room的時候替換了它,或者push 一個新的同名檔案。