/* * Copyright (C) 2017 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.tv.audio; import android.app.Activity; import android.content.Context; import android.media.AudioAttributes; import android.media.AudioFocusRequest; import android.media.AudioManager; import android.os.Build; import android.support.annotation.Nullable; import com.android.tv.features.TvFeatures; import com.android.tv.ui.api.TunableTvViewPlayingApi; /** A helper class to help {@code Activities} to handle audio-related stuffs. */ public class AudioManagerHelper implements AudioManager.OnAudioFocusChangeListener { private static final float AUDIO_MAX_VOLUME = 1.0f; private static final float AUDIO_MIN_VOLUME = 0.0f; private static final float AUDIO_DUCKING_VOLUME = 0.3f; private final Activity mActivity; private final TunableTvViewPlayingApi mTvView; private final AudioManager mAudioManager; @Nullable private final AudioFocusRequest mFocusRequest; private int mAudioFocusStatus = AudioManager.AUDIOFOCUS_NONE; public AudioManagerHelper(Activity activity, TunableTvViewPlayingApi tvView) { mActivity = activity; mTvView = tvView; mAudioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes( new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE) .setUsage(AudioAttributes.USAGE_MEDIA) .build()) .setOnAudioFocusChangeListener(this) // Auto ducking from the system does not mute the TV Input Service. // Using will pause when ducked allows us to set the stream volume // even when we are not pausing. .setWillPauseWhenDucked(true) .build(); } else { mFocusRequest = null; } } /** * Sets suitable volume to {@link TunableTvViewPlayingApi} according to the current audio focus. * *

If the focus status is {@link AudioManager#AUDIOFOCUS_LOSS} or {@link * AudioManager#AUDIOFOCUS_NONE} and the activity is under PIP mode, this method will finish the * activity. Sets suitable volume to {@link TunableTvViewPlayingApi} according to the current * audio focus. If the focus status is {@link AudioManager#AUDIOFOCUS_LOSS} and the activity is * under PIP mode, this method will finish the activity. */ public void setVolumeByAudioFocusStatus() { if (mTvView.isPlaying()) { switch (mAudioFocusStatus) { case AudioManager.AUDIOFOCUS_GAIN: if (mTvView.isTimeShiftAvailable()) { mTvView.timeShiftPlay(); } else { mTvView.setStreamVolume(AUDIO_MAX_VOLUME); } break; case AudioManager.AUDIOFOCUS_NONE: case AudioManager.AUDIOFOCUS_LOSS: if (TvFeatures.PICTURE_IN_PICTURE.isEnabled(mActivity) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && mActivity.isInPictureInPictureMode()) { mActivity.finish(); break; } // fall through case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: if (mTvView.isTimeShiftAvailable()) { mTvView.timeShiftPause(); } else { mTvView.setStreamVolume(AUDIO_MIN_VOLUME); } break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: if (mTvView.isTimeShiftAvailable()) { mTvView.timeShiftPause(); } else { mTvView.setStreamVolume(AUDIO_DUCKING_VOLUME); } break; } } } /** * Tries to request audio focus from {@link AudioManager} and set volume according to the * returned result. */ public void requestAudioFocus() { int result; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { result = mAudioManager.requestAudioFocus(mFocusRequest); } else { result = mAudioManager.requestAudioFocus( this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); } mAudioFocusStatus = (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) ? AudioManager.AUDIOFOCUS_GAIN : AudioManager.AUDIOFOCUS_LOSS; setVolumeByAudioFocusStatus(); } /** Abandons audio focus. */ public void abandonAudioFocus() { mAudioFocusStatus = AudioManager.AUDIOFOCUS_LOSS; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mAudioManager.abandonAudioFocusRequest(mFocusRequest); } else { mAudioManager.abandonAudioFocus(this); } } @Override public void onAudioFocusChange(int focusChange) { mAudioFocusStatus = focusChange; setVolumeByAudioFocusStatus(); } }