Android監(jiān)聽靠近聽筒厌丑,音頻播放切換聽筒和外放

android.hardware提供了距離感應(yīng)器監(jiān)聽器定欧,通過(guò)注冊(cè)監(jiān)聽,來(lái)判斷是否靠近聽筒怒竿。

private void registerProximitySensorListener() {
        if (getActivity() == null) {
            return;
        }
        sensorManager = (SensorManager) getActivity().getSystemService(SENSOR_SERVICE);
        if (sensorManager == null) {
            return;
        }
        sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
        sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
    }
@Override
    public void onSensorChanged(SensorEvent event) {
        if (audioManager == null) {
            return;
        }
        if (isHeadphonesPlugged()) {
            // 如果耳機(jī)已插入砍鸠,設(shè)置距離傳感器失效
            return;
        }
        if (DEBUG) {
            Log.i(TAG, "onSensorChanged: " + MediaManager.isPlaying() + " event.values[0]: " + event.values[0]);
        }
        if (MediaManager.isPlaying()) {
            // 如果音頻正在播放
            float distance = event.values[0];
            if (distance >= sensor.getMaximumRange()) {
                // 用戶遠(yuǎn)離聽筒,音頻外放耕驰,亮屏
                changeToSpeaker();
                if (DEBUG) {
                    Log.i(TAG, "onSensorChanged: 外放");
                }
            } else {
                MediaManager.reStart();
                // 用戶貼近聽筒爷辱,切換音頻到聽筒輸出,并且熄屏防誤觸
                changeToReceiver();
                if (DEBUG) {
                    Log.i(TAG, "onSensorChanged: 聽筒");
                }
                audioManager.setSpeakerphoneOn(false);
            }
        } else {
            // 音頻播放完了
            changeToSpeaker();
        }

    }

MediaManager是播放器單例朦肘,維護(hù)了一個(gè)MediaPlayer,用來(lái)重新處理播放器托嚣。

Android切換聽筒時(shí),不同于IOS手機(jī)厚骗,Android會(huì)有延時(shí),所以建議切換聽筒
前兢哭,讓音頻重新播放领舰。

public static void reStart() {
        if (mPlayer != null) {
            try {
                mPlayer.seekTo(0);
                mPlayer.start();
            } catch (Exception e) {
                if (DEBUG) {
                    Log.i(TAG, "reStart: " + e.getMessage());
                }
                e.printStackTrace();
            }

        }
    }

當(dāng)靠近聽筒后,通過(guò)PowerManager來(lái)熄滅屏幕迟螺,以免誤觸


private PowerManager.WakeLock wakeLock;
private final PowerManager mPowerManager;


private void setScreenOff() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Log.i(TAG, "setScreenOff: 熄滅屏幕");
            if (wakeLock == null) {
                wakeLock = mPowerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
            }
            wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/);
        }
    }

private void setScreenOn() {
        if (wakeLock != null) {
            wakeLock.setReferenceCounted(false);
            wakeLock.release();
            wakeLock = null;
        }
    }

切換聽筒和外放

/**
     * 切換到外放
     */
    public void changeToSpeaker() {
        setScreenOn();
        if (audioManager == null) {
            return;
        }
        audioManager.setMode(AudioManager.MODE_NORMAL);
        audioManager.setSpeakerphoneOn(true);
    }

    /**
     * 切換到耳機(jī)模式
     */
    public void changeToHeadset() {
        if (audioManager == null) {
            return;
        }
        audioManager.setSpeakerphoneOn(false);
    }

    /**
     * 切換到聽筒
     */
    public void changeToReceiver() {
        setScreenOff();
        if (audioManager == null) {
            return;
        }
        audioManager.setSpeakerphoneOn(false);
        audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
    }

當(dāng)手機(jī)插入耳機(jī)冲秽,不做任何操作。

判斷是否插入耳機(jī)矩父,isWiredHeadsetOn()已過(guò)時(shí)锉桑,根據(jù)版本采用最新的判斷方式

private boolean isHeadphonesPlugged() {
        if (audioManager == null) {
            return false;
        }
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            AudioDeviceInfo[] audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
            for (AudioDeviceInfo deviceInfo : audioDevices) {
                if (deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES
                        || deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
                    return true;
                }
            }
            return false;
        } else {
            return audioManager.isWiredHeadsetOn();
        }
    }

通過(guò)LifecycleObserver綁定Activity的生命周期,釋放資源

//可以監(jiān)聽生命周期
        if (getActivity() != null) {
            getActivity().getLifecycle().addObserver(this);
        }

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume() {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
        sensorManager.unregisterListener(this);
        sensorManager = null;
        wakeLock = null;
        mActivity = null;
    }

綜上窍株,代碼如下

import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.os.Build;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import java.lang.ref.WeakReference;

import static android.content.Context.POWER_SERVICE;
import static android.content.Context.SENSOR_SERVICE;
/**
 * Created by walkingMen on 2018/12/24.
 */
public class AudioSensorBinder implements LifecycleObserver, SensorEventListener {
    private final AudioManager audioManager;
    private final PowerManager mPowerManager;
    @Nullable
    private WeakReference<AppCompatActivity> mActivity;
    private SensorManager sensorManager;
    private Sensor sensor;
    private PowerManager.WakeLock wakeLock;

    public AudioSensorBinder(@NonNull AppCompatActivity activity) {
        mActivity = new WeakReference<>(activity);
        //可以監(jiān)聽生命周期
        if (getActivity() != null) {
            getActivity().getLifecycle().addObserver(this);
        }
        audioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
        mPowerManager = (PowerManager) activity.getSystemService(POWER_SERVICE);
        registerProximitySensorListener();

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void onResume() {

    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void onDestroy() {
        sensorManager.unregisterListener(this);
        sensorManager = null;
        wakeLock = null;
        mActivity = null;
    }

    /**
     * 注冊(cè)距離感應(yīng)器監(jiān)聽器民轴,監(jiān)測(cè)用戶是否靠近手機(jī)聽筒
     */
    private void registerProximitySensorListener() {
        if (getActivity() == null) {
            return;
        }
        sensorManager = (SensorManager) getActivity().getSystemService(SENSOR_SERVICE);
        if (sensorManager == null) {
            return;
        }
        sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
        sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
    }

    private AppCompatActivity getActivity() {
        if (mActivity != null) {
            return mActivity.get();
        }
        return null;
    }

    private static final String TAG = "AudioSensorBinder";

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (audioManager == null) {
            return;
        }
        if (isHeadphonesPlugged()) {
            // 如果耳機(jī)已插入,設(shè)置距離傳感器失效
            return;
        }
        if (DEBUG) {
            Log.i(TAG, "onSensorChanged: " + MediaManager.isPlaying() + " event.values[0]: " + event.values[0]);
        }
        if (MediaManager.isPlaying()) {
            // 如果音頻正在播放
            float distance = event.values[0];
            if (distance >= sensor.getMaximumRange()) {
                // 用戶遠(yuǎn)離聽筒球订,音頻外放后裸,亮屏
                changeToSpeaker();
                if (DEBUG) {
                    Log.i(TAG, "onSensorChanged: 外放");
                }
            } else {
                MediaManager.reStart();
                // 用戶貼近聽筒,切換音頻到聽筒輸出冒滩,并且熄屏防誤觸
                changeToReceiver();
                if (DEBUG) {
                    Log.i(TAG, "onSensorChanged: 聽筒");
                }
                audioManager.setSpeakerphoneOn(false);
            }
        } else {
            // 音頻播放完了
            changeToSpeaker();
        }

    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    private boolean isHeadphonesPlugged() {
        if (audioManager == null) {
            return false;
        }
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            AudioDeviceInfo[] audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
            for (AudioDeviceInfo deviceInfo : audioDevices) {
                if (deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES
                        || deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
                    return true;
                }
            }
            return false;
        } else {
            return audioManager.isWiredHeadsetOn();
        }
    }


    /**
     * 切換到外放
     */
    public void changeToSpeaker() {
        setScreenOn();
        if (audioManager == null) {
            return;
        }
        audioManager.setMode(AudioManager.MODE_NORMAL);
        audioManager.setSpeakerphoneOn(true);
    }

    /**
     * 切換到耳機(jī)模式
     */
    public void changeToHeadset() {
        if (audioManager == null) {
            return;
        }
        audioManager.setSpeakerphoneOn(false);
    }

    /**
     * 切換到聽筒
     */
    public void changeToReceiver() {
        setScreenOff();
        if (audioManager == null) {
            return;
        }
        audioManager.setSpeakerphoneOn(false);
        audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
    }

    private void setScreenOff() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Log.i(TAG, "setScreenOff: 熄滅屏幕");
            if (wakeLock == null) {
                wakeLock = mPowerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
            }
            wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/);
        }
    }

    private void setScreenOn() {
        if (wakeLock != null) {
            wakeLock.setReferenceCounted(false);
            wakeLock.release();
            wakeLock = null;
        }
    }

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末微驶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子开睡,更是在濱河造成了極大的恐慌因苹,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篇恒,死亡現(xiàn)場(chǎng)離奇詭異扶檐,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)胁艰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門蘸秘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)官卡,“玉大人,你說(shuō)我怎么就攤上這事醋虏⊙爸洌” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵颈嚼,是天一觀的道長(zhǎng)毛秘。 經(jīng)常有香客問我,道長(zhǎng)阻课,這世上最難降的妖魔是什么叫挟? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮限煞,結(jié)果婚禮上抹恳,老公的妹妹穿的比我還像新娘。我一直安慰自己署驻,他們只是感情好奋献,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著旺上,像睡著了一般瓶蚂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宣吱,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天窃这,我揣著相機(jī)與錄音,去河邊找鬼征候。 笑死杭攻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的疤坝。 我是一名探鬼主播朴上,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼卒煞!你這毒婦竟也來(lái)了痪宰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后偷遗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體望迎,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扛点,死狀恐怖哥遮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情陵究,我是刑警寧澤眠饮,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站铜邮,受9級(jí)特大地震影響仪召,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜松蒜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一扔茅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧秸苗,春花似錦召娜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至胁后,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嗦枢,已是汗流浹背攀芯。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留文虏,地道東北人侣诺。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像氧秘,于是被迫代替她去往敵國(guó)和親年鸳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容