1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.android.car.kitchensink.audio; 18 19 import static android.R.layout.simple_spinner_dropdown_item; 20 import static android.R.layout.simple_spinner_item; 21 import static android.car.CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION; 22 import static android.car.CarAppFocusManager.APP_FOCUS_TYPE_VOICE_COMMAND; 23 import static android.car.media.CarAudioManager.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID; 24 import static android.car.media.CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING; 25 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE; 26 import static android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; 27 import static android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION; 28 import static android.media.AudioAttributes.USAGE_ASSISTANT; 29 import static android.media.AudioAttributes.USAGE_MEDIA; 30 import static android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION; 31 import static android.media.AudioManager.AUDIOFOCUS_GAIN; 32 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT; 33 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE; 34 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; 35 import static android.media.AudioManager.AUDIOFOCUS_LOSS; 36 import static android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT; 37 import static android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK; 38 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_DELAYED; 39 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 40 import static android.media.AudioManager.GET_DEVICES_INPUTS; 41 import static android.os.Build.IS_EMULATOR; 42 43 import static com.google.android.car.kitchensink.R.raw.free_flight; 44 import static com.google.android.car.kitchensink.R.raw.one2six; 45 import static com.google.android.car.kitchensink.R.raw.ring_classic_01; 46 import static com.google.android.car.kitchensink.R.raw.turnright; 47 import static com.google.android.car.kitchensink.R.raw.well_worth_the_wait; 48 import static com.google.android.car.kitchensink.audio.AudioPlayer.PLAYER_STATE_COMPLETED; 49 50 import android.car.Car; 51 import android.car.CarAppFocusManager; 52 import android.car.CarAppFocusManager.OnAppFocusChangedListener; 53 import android.car.CarAppFocusManager.OnAppFocusOwnershipCallback; 54 import android.car.CarOccupantZoneManager; 55 import android.car.media.CarAudioManager; 56 import android.car.media.CarAudioZoneConfigInfo; 57 import android.car.media.SwitchAudioZoneConfigCallback; 58 import android.content.Context; 59 import android.content.pm.ApplicationInfo; 60 import android.content.pm.PackageManager; 61 import android.media.AudioAttributes; 62 import android.media.AudioDeviceInfo; 63 import android.media.AudioFocusRequest; 64 import android.media.AudioManager; 65 import android.media.AudioManager.OnAudioFocusChangeListener; 66 import android.media.AudioRouting; 67 import android.media.HwAudioSource; 68 import android.os.Bundle; 69 import android.os.Handler; 70 import android.os.Looper; 71 import android.util.Log; 72 import android.view.KeyEvent; 73 import android.view.LayoutInflater; 74 import android.view.View; 75 import android.view.ViewGroup; 76 import android.widget.AdapterView; 77 import android.widget.ArrayAdapter; 78 import android.widget.Button; 79 import android.widget.LinearLayout; 80 import android.widget.RadioGroup; 81 import android.widget.Spinner; 82 import android.widget.TextView; 83 84 import androidx.annotation.NonNull; 85 import androidx.core.content.ContextCompat; 86 import androidx.fragment.app.Fragment; 87 import androidx.viewpager.widget.ViewPager; 88 89 import com.android.internal.util.Preconditions; 90 91 import com.google.android.car.kitchensink.R; 92 import com.google.android.car.kitchensink.audio.AudioPlayer.PlayStateListener; 93 import com.google.android.material.tabs.TabLayout; 94 95 import java.util.ArrayList; 96 import java.util.List; 97 98 import javax.annotation.concurrent.GuardedBy; 99 100 public class AudioTestFragment extends Fragment { 101 public static final String FRAGMENT_NAME = "audio"; 102 private static final String TAG = "CAR.AUDIO.KS"; 103 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 104 105 private static final long STOP_DELAY_TIME_MS = 3_000; 106 107 // Key for communicating to hall which audio zone has been selected to play 108 private static final String AAE_PARAMETER_KEY_FOR_SELECTED_ZONE = 109 "com.android.car.emulator.selected_zone"; 110 111 private static final Integer[] TONES = new Integer[] { 200, 400, 600, 800, 1_000, 1_200 }; 112 public static final String DEVICE_SELECTED_NONE = "None"; 113 114 private AudioManager mAudioManager; 115 private FocusHandler mAudioFocusHandler; 116 117 private AudioPlayer mMusicPlayer; 118 @GuardedBy("mLock") 119 private AudioPlayer mMusicPlayerWithDelayedFocus; 120 private AudioPlayer mMusicPlayerShort; 121 private AudioPlayer mNavGuidancePlayer; 122 private AudioPlayer mPhoneAudioPlayer; 123 private AudioPlayer mVrPlayer; 124 private AudioPlayer mSystemPlayer; 125 private AudioPlayer mWavPlayer; 126 private AudioPlayer mMusicPlayerForSelectedDeviceAddress; 127 private HwAudioSource mHwAudioSource; 128 private AudioPlayer[] mAllPlayers; 129 130 @GuardedBy("mLock") 131 private AudioTrackPlayer mAudioTrackPlayer; 132 133 @GuardedBy("mLock") 134 private CarAudioZoneConfigInfo mZoneConfigInfoSelected; 135 136 private Handler mHandler; 137 private Context mContext; 138 139 private Car mCar; 140 private CarAppFocusManager mAppFocusManager; 141 private AudioAttributes mMusicAudioAttrib; 142 private AudioAttributes mNavAudioAttrib; 143 private AudioAttributes mPhoneAudioAttrib; 144 private AudioAttributes mVrAudioAttrib; 145 private AudioAttributes mRadioAudioAttrib; 146 private AudioAttributes mSystemSoundAudioAttrib; 147 private AudioAttributes mMusicAudioAttribForDeviceAddress; 148 private CarAudioManager mCarAudioManager; 149 private Spinner mZoneSpinner; 150 private ArrayAdapter<Integer> mZoneAdapter; 151 private Spinner mDeviceAddressSpinner; 152 private ArrayAdapter<CarAudioZoneDeviceInfo> mDeviceAddressAdapter; 153 private LinearLayout mDeviceAddressLayout; 154 private Spinner mZoneConfigurationSpinner; 155 private LinearLayout mZoneConfigurationLayout; 156 private ArrayAdapter<CarAudioZoneConfigInfoWrapper> mZoneConfigurationAdapter; 157 @GuardedBy("mLock") 158 private TextView mCurrentZoneConfigurationView; 159 160 private TabLayout mPlayerTabLayout; 161 private ViewPager mViewPager; 162 private CarAudioZoneTabAdapter mAudioPlayerZoneAdapter; 163 164 private final Object mLock = new Object(); 165 166 @GuardedBy("mLock") 167 private AudioFocusRequest mDelayedFocusRequest; 168 private OnAudioFocusChangeListener mMediaWithDelayedFocusListener; 169 private TextView mDelayedStatusText; 170 private TextView mDelayedAudioDeviceText; 171 172 private final OnAudioFocusChangeListener mNavFocusListener = (focusChange) -> { 173 Log.i(TAG, "Nav focus change:" + focusChange); 174 }; 175 private final OnAudioFocusChangeListener mVrFocusListener = (focusChange) -> { 176 Log.i(TAG, "VR focus change:" + focusChange); 177 }; 178 private final OnAudioFocusChangeListener mRadioFocusListener = (focusChange) -> { 179 Log.i(TAG, "Radio focus change:" + focusChange); 180 }; 181 182 private final CarAppFocusManager.OnAppFocusOwnershipCallback mOwnershipCallbacks = 183 new OnAppFocusOwnershipCallback() { 184 @Override 185 public void onAppFocusOwnershipLost(int focus) { 186 } 187 @Override 188 public void onAppFocusOwnershipGranted(int focus) { 189 } 190 }; 191 192 private final SwitchAudioZoneConfigCallback mSwitchAudioZoneConfigCallback = 193 new SwitchAudioZoneConfigCallback() { 194 @Override 195 public void onAudioZoneConfigSwitched(@NonNull CarAudioZoneConfigInfo zoneConfig, 196 boolean isSuccessful) { 197 Log.i(TAG, "Car audio zone switching to " + zoneConfig + " successful? " 198 + isSuccessful); 199 if (!isSuccessful) { 200 return; 201 } 202 synchronized (mLock) { 203 mCurrentZoneConfigurationView.setText(zoneConfig.getName()); 204 } 205 updateDeviceAddressPlayer(); 206 } 207 }; 208 209 private final PlayStateListener mNavigationStateListener = (state) -> { 210 if (state == PLAYER_STATE_COMPLETED) { 211 mAppFocusManager.abandonAppFocus(mOwnershipCallbacks, APP_FOCUS_TYPE_NAVIGATION); 212 } 213 }; 214 215 private VolumeKeyEventsButtonManager mVolumeKeyEventHandler; 216 connectCar()217 private void connectCar() { 218 mContext = getContext(); 219 mHandler = new Handler(Looper.getMainLooper()); 220 mCar = Car.createCar(mContext, /* handler= */ null, 221 Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, (car, ready) -> { 222 if (!ready) { 223 return; 224 } 225 mAppFocusManager = 226 (CarAppFocusManager) car.getCarManager(Car.APP_FOCUS_SERVICE); 227 OnAppFocusChangedListener listener = new OnAppFocusChangedListener() { 228 @Override 229 public void onAppFocusChanged(int appType, boolean active) { 230 } 231 }; 232 mAppFocusManager.addFocusListener(listener, APP_FOCUS_TYPE_NAVIGATION); 233 mAppFocusManager.addFocusListener(listener, APP_FOCUS_TYPE_VOICE_COMMAND); 234 235 mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE); 236 237 handleSetUpZoneSelection(); 238 239 handleSetUpZoneConfigurationSelection(); 240 241 setUpDeviceAddressPlayer(); 242 }); 243 } 244 245 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle)246 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) { 247 Log.i(TAG, "onCreateView"); 248 View view = inflater.inflate(R.layout.audio, container, false); 249 //Zone Spinner 250 setUpZoneSpinnerView(view); 251 252 setUpZoneConfigurationSpinnerView(view); 253 254 setUpDeviceAddressLayoutView(view); 255 256 connectCar(); 257 setUpTrackToneSpinnerView(view); 258 setUpCarSoundsLayouts(view); 259 initializePlayers(); 260 261 TextView currentZoneIdTextView = view.findViewById(R.id.activity_current_zone); 262 setActivityCurrentZoneId(currentZoneIdTextView); 263 264 mAudioManager = (AudioManager) mContext.getSystemService( 265 Context.AUDIO_SERVICE); 266 mAudioFocusHandler = new FocusHandler( 267 view.findViewById(R.id.button_focus_request_selection), 268 view.findViewById(R.id.button_audio_focus_request), 269 view.findViewById(R.id.text_audio_focus_state)); 270 view.findViewById(R.id.button_media_play_start).setOnClickListener(v -> { 271 boolean requestFocus = true; 272 boolean repeat = true; 273 mMusicPlayer.start(requestFocus, repeat, AUDIOFOCUS_GAIN); 274 }); 275 view.findViewById(R.id.button_media_play_once).setOnClickListener(v -> { 276 mMusicPlayerShort.start(true, false, AUDIOFOCUS_GAIN_TRANSIENT); 277 mHandler.postDelayed(() -> mMusicPlayerShort.stop(), STOP_DELAY_TIME_MS); 278 }); 279 view.findViewById(R.id.button_media_play_stop).setOnClickListener(v -> mMusicPlayer.stop()); 280 view.findViewById(R.id.button_wav_play_start).setOnClickListener( 281 v -> mWavPlayer.start(true, true, AUDIOFOCUS_GAIN)); 282 view.findViewById(R.id.button_wav_play_stop).setOnClickListener(v -> mWavPlayer.stop()); 283 view.findViewById(R.id.button_nav_play_once).setOnClickListener(v -> { 284 if (mAppFocusManager == null) { 285 Log.e(TAG, "mAppFocusManager is null"); 286 return; 287 } 288 if (DBG) { 289 Log.i(TAG, "Nav start"); 290 } 291 mAppFocusManager.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, mOwnershipCallbacks); 292 if (!mNavGuidancePlayer.isPlaying()) { 293 mNavGuidancePlayer.start(true, false, 294 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, mNavigationStateListener); 295 } 296 }); 297 view.findViewById(R.id.button_vr_play_once).setOnClickListener(v -> { 298 if (mAppFocusManager == null) { 299 Log.e(TAG, "mAppFocusManager is null"); 300 return; 301 } 302 if (DBG) { 303 Log.i(TAG, "VR start"); 304 } 305 mAppFocusManager.requestAppFocus(APP_FOCUS_TYPE_VOICE_COMMAND, mOwnershipCallbacks); 306 if (!mVrPlayer.isPlaying()) { 307 mVrPlayer.start(true, false, 308 AUDIOFOCUS_GAIN_TRANSIENT, mNavigationStateListener); 309 } 310 }); 311 view.findViewById(R.id.button_system_play_once).setOnClickListener(v -> { 312 if (DBG) { 313 Log.i(TAG, "System start"); 314 } 315 if (!mSystemPlayer.isPlaying()) { 316 // system sound played without focus 317 mSystemPlayer.start(false, false, 0); 318 } 319 }); 320 view.findViewById(R.id.button_nav_start).setOnClickListener(v -> handleNavStart()); 321 view.findViewById(R.id.button_nav_end).setOnClickListener(v -> handleNavEnd()); 322 view.findViewById(R.id.button_vr_start).setOnClickListener(v -> handleVrStart()); 323 view.findViewById(R.id.button_vr_end).setOnClickListener(v -> handleVrEnd()); 324 view.findViewById(R.id.button_radio_start).setOnClickListener(v -> handleRadioStart()); 325 view.findViewById(R.id.button_radio_end).setOnClickListener(v -> handleRadioEnd()); 326 view.findViewById(R.id.button_speaker_phone_on).setOnClickListener( 327 v -> mAudioManager.setSpeakerphoneOn(true)); 328 view.findViewById(R.id.button_speaker_phone_off).setOnClickListener( 329 v -> mAudioManager.setSpeakerphoneOn(false)); 330 view.findViewById(R.id.button_microphone_on).setOnClickListener( 331 v -> mAudioManager.setMicrophoneMute(false)); 332 view.findViewById(R.id.button_microphone_off).setOnClickListener( 333 v -> mAudioManager.setMicrophoneMute(true)); 334 final View hwAudioSourceNotFound = view.findViewById(R.id.hw_audio_source_not_found); 335 final View hwAudioSourceStart = view.findViewById(R.id.hw_audio_source_start); 336 final View hwAudioSourceStop = view.findViewById(R.id.hw_audio_source_stop); 337 if (mHwAudioSource == null) { 338 hwAudioSourceNotFound.setVisibility(View.VISIBLE); 339 hwAudioSourceStart.setVisibility(View.GONE); 340 hwAudioSourceStop.setVisibility(View.GONE); 341 } else { 342 hwAudioSourceNotFound.setVisibility(View.GONE); 343 hwAudioSourceStart.setVisibility(View.VISIBLE); 344 hwAudioSourceStop.setVisibility(View.VISIBLE); 345 view.findViewById(R.id.hw_audio_source_start).setOnClickListener( 346 v -> handleHwAudioSourceStart()); 347 view.findViewById(R.id.hw_audio_source_stop).setOnClickListener( 348 v -> handleHwAudioSourceStop()); 349 } 350 351 // Manage buttons for audio player for device address 352 view.findViewById(R.id.button_device_media_play_start).setOnClickListener(v -> { 353 startDeviceAudio(); 354 }); 355 view.findViewById(R.id.button_device_media_play_once).setOnClickListener(v -> { 356 startDeviceAudio(); 357 mHandler.postDelayed( 358 () -> mMusicPlayerForSelectedDeviceAddress.stop(), STOP_DELAY_TIME_MS); 359 }); 360 view.findViewById(R.id.button_device_media_play_stop) 361 .setOnClickListener(v -> mMusicPlayerForSelectedDeviceAddress.stop()); 362 363 view.findViewById(R.id.media_delayed_focus_start) 364 .setOnClickListener(v -> handleDelayedMediaStart()); 365 view.findViewById(R.id.media_delayed_focus_stop) 366 .setOnClickListener(v -> handleDelayedMediaStop()); 367 368 view.findViewById(R.id.phone_audio_focus_start) 369 .setOnClickListener(v -> mPhoneAudioPlayer.start(true, true, 370 AUDIOFOCUS_GAIN_TRANSIENT)); 371 view.findViewById(R.id.phone_audio_focus_stop) 372 .setOnClickListener(v -> mPhoneAudioPlayer.stop()); 373 374 view.findViewById(R.id.track_audio_start) 375 .setOnClickListener(v-> startAudioTrack()); 376 view.findViewById(R.id.track_audio_stop) 377 .setOnClickListener(v -> stopAudioTrack()); 378 379 mDelayedStatusText = view.findViewById(R.id.media_delayed_player_status); 380 mDelayedAudioDeviceText = view.findViewById(R.id.media_delayed_player_device); 381 resetDeviceSelectedForDelayedMedia(); 382 383 mVolumeKeyEventHandler = new VolumeKeyEventsButtonManager( 384 mCar.getCarManager(CarOccupantZoneManager.class)); 385 386 Button upButton = view.findViewById(R.id.volume_plus_key_event_button); 387 upButton.setOnClickListener((v) -> mVolumeKeyEventHandler 388 .sendClickEvent(KeyEvent.KEYCODE_VOLUME_UP)); 389 390 Button downButton = view.findViewById(R.id.volume_minus_key_event_button); 391 downButton.setOnClickListener( 392 (v) -> mVolumeKeyEventHandler 393 .sendClickEvent(KeyEvent.KEYCODE_VOLUME_DOWN)); 394 395 Button muteButton = view.findViewById(R.id.volume_mute_key_event_button); 396 muteButton.setOnClickListener( 397 (v) -> mVolumeKeyEventHandler 398 .sendClickEvent(KeyEvent.KEYCODE_VOLUME_MUTE)); 399 400 Button zoneConfigSwitchButton = 401 view.findViewById(R.id.switch_zone_configuration_key_event_button); 402 zoneConfigSwitchButton.setOnClickListener( 403 (v) -> switchToZoneConfigSelected()); 404 405 return view; 406 } 407 startAudioTrack()408 private void startAudioTrack() { 409 synchronized (mLock) { 410 mAudioTrackPlayer.start(); 411 } 412 } 413 stopAudioTrack()414 private void stopAudioTrack() { 415 synchronized (mLock) { 416 mAudioTrackPlayer.stop(); 417 } 418 } 419 420 @Override onDestroyView()421 public void onDestroyView() { 422 Log.i(TAG, "onDestroyView"); 423 for (AudioPlayer p : mAllPlayers) { 424 p.stop(); 425 } 426 if (mMusicPlayerForSelectedDeviceAddress != null) { 427 mMusicPlayerForSelectedDeviceAddress.stop(); 428 mMusicPlayerForSelectedDeviceAddress = null; 429 } 430 stopAudioTrack(); 431 handleHwAudioSourceStop(); 432 if (mAudioFocusHandler != null) { 433 mAudioFocusHandler.release(); 434 mAudioFocusHandler = null; 435 } 436 if (mDelayedFocusRequest != null) { 437 mAudioManager.abandonAudioFocusRequest(mDelayedFocusRequest); 438 } 439 if (mAppFocusManager != null) { 440 mAppFocusManager.abandonAppFocus(mOwnershipCallbacks); 441 } 442 if (mCar != null && mCar.isConnected()) { 443 mCar.disconnect(); 444 mCar = null; 445 } 446 super.onDestroyView(); 447 } 448 initializePlayers()449 private void initializePlayers() { 450 mMusicAudioAttrib = new AudioAttributes.Builder() 451 .setUsage(USAGE_MEDIA) 452 .build(); 453 mNavAudioAttrib = new AudioAttributes.Builder() 454 .setUsage(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE) 455 .build(); 456 mPhoneAudioAttrib = new AudioAttributes.Builder() 457 .setUsage(USAGE_VOICE_COMMUNICATION) 458 .build(); 459 mVrAudioAttrib = new AudioAttributes.Builder() 460 .setUsage(USAGE_ASSISTANT) 461 .build(); 462 mRadioAudioAttrib = new AudioAttributes.Builder() 463 .setUsage(USAGE_MEDIA) 464 .build(); 465 mSystemSoundAudioAttrib = new AudioAttributes.Builder() 466 .setUsage(USAGE_ASSISTANCE_SONIFICATION) 467 .build(); 468 // Create an audio device address audio attribute 469 mMusicAudioAttribForDeviceAddress = new AudioAttributes.Builder() 470 .setUsage(USAGE_MEDIA) 471 .build(); 472 473 474 mMusicPlayerForSelectedDeviceAddress = new AudioPlayer(mContext, well_worth_the_wait, 475 mMusicAudioAttribForDeviceAddress); 476 mMusicPlayer = new AudioPlayer(mContext, well_worth_the_wait, mMusicAudioAttrib); 477 mMusicPlayerWithDelayedFocus = new AudioPlayer(mContext, well_worth_the_wait, 478 mMusicAudioAttrib, /* preferredDeviceInfo= */ null, 479 router -> setRoutedDeviceForDelayedPlayer(router)); 480 mMusicPlayerShort = new AudioPlayer(mContext, ring_classic_01, mMusicAudioAttrib); 481 mNavGuidancePlayer = new AudioPlayer(mContext, turnright, mNavAudioAttrib); 482 mPhoneAudioPlayer = new AudioPlayer(mContext, free_flight, mPhoneAudioAttrib); 483 mVrPlayer = new AudioPlayer(mContext, one2six, mVrAudioAttrib); 484 mSystemPlayer = new AudioPlayer(mContext, ring_classic_01, mSystemSoundAudioAttrib); 485 mWavPlayer = new AudioPlayer(mContext, free_flight, mMusicAudioAttrib); 486 final AudioDeviceInfo tuner = findTunerDevice(mContext); 487 if (tuner != null) { 488 mHwAudioSource = new HwAudioSource.Builder() 489 .setAudioAttributes(mMusicAudioAttrib) 490 .setAudioDeviceInfo(findTunerDevice(mContext)) 491 .build(); 492 } 493 mAllPlayers = new AudioPlayer[] { 494 mMusicPlayer, 495 mMusicPlayerShort, 496 mNavGuidancePlayer, 497 mVrPlayer, 498 mSystemPlayer, 499 mWavPlayer, 500 mMusicPlayerWithDelayedFocus, 501 mPhoneAudioPlayer 502 }; 503 504 mAudioTrackPlayer = new AudioTrackPlayer(getTone(0)); 505 } 506 setRoutedDeviceForDelayedPlayer(AudioRouting router)507 private void setRoutedDeviceForDelayedPlayer(AudioRouting router) { 508 Log.i(TAG, "setRoutedDeviceForDelayedPlayer: " + router); 509 String deviceAddress = "Does not exist"; 510 if (router.getRoutedDevice() != null) { 511 deviceAddress = router.getRoutedDevice().getAddress(); 512 } 513 514 mDelayedAudioDeviceText.setText(getString(R.string.audio_device_selected, deviceAddress)); 515 } 516 setActivityCurrentZoneId(TextView currentZoneIdTextView)517 private void setActivityCurrentZoneId(TextView currentZoneIdTextView) { 518 if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) { 519 try { 520 int audioZoneId = getCurrentZoneId(); 521 currentZoneIdTextView.setText(Integer.toString(audioZoneId)); 522 } catch (PackageManager.NameNotFoundException e) { 523 Log.e(TAG, "setActivityCurrentZoneId Failed to find name: " , e); 524 } 525 } 526 } 527 handleDelayedMediaStart()528 private void handleDelayedMediaStart() { 529 synchronized (mLock) { 530 if (mDelayedFocusRequest != null) { 531 return; 532 } 533 mMediaWithDelayedFocusListener = new MediaWithDelayedFocusListener(); 534 mDelayedFocusRequest = new AudioFocusRequest 535 .Builder(AUDIOFOCUS_GAIN) 536 .setAudioAttributes(mMusicAudioAttrib) 537 .setOnAudioFocusChangeListener(mMediaWithDelayedFocusListener) 538 .setForceDucking(false) 539 .setWillPauseWhenDucked(false) 540 .setAcceptsDelayedFocusGain(true) 541 .build(); 542 int delayedFocusRequestResults = mAudioManager.requestAudioFocus(mDelayedFocusRequest); 543 if (delayedFocusRequestResults == AUDIOFOCUS_REQUEST_GRANTED) { 544 startDelayedMediaPlayerLocked(); 545 return; 546 } 547 if (delayedFocusRequestResults == AUDIOFOCUS_REQUEST_DELAYED) { 548 if (DBG) Log.d(TAG, "Media With Delayed Focus delayed focus granted"); 549 mDelayedStatusText.setText(R.string.player_delayed); 550 resetDeviceSelectedForDelayedMedia(); 551 return; 552 } 553 mMediaWithDelayedFocusListener = null; 554 mDelayedFocusRequest = null; 555 mDelayedStatusText.setText(R.string.player_not_started); 556 resetDeviceSelectedForDelayedMedia(); 557 } 558 if (DBG) Log.d(TAG, "Media With Delayed Focus focus rejected"); 559 } 560 startDelayedMediaPlayerLocked()561 private void startDelayedMediaPlayerLocked() { 562 if (!mMusicPlayerWithDelayedFocus.isPlaying()) { 563 if (DBG) Log.d(TAG, "Media With Delayed Focus starting player"); 564 mMusicPlayerWithDelayedFocus.start(false, true, 565 AUDIOFOCUS_GAIN); 566 mDelayedStatusText.setText(R.string.player_started); 567 return; 568 } 569 if (DBG) Log.d(TAG, "Media With Delayed Focus player already started"); 570 } 571 handleDelayedMediaStop()572 private void handleDelayedMediaStop() { 573 synchronized (mLock) { 574 if (mDelayedFocusRequest != null) { 575 int requestResults = mAudioManager.abandonAudioFocusRequest(mDelayedFocusRequest); 576 if (DBG) { 577 Log.d(TAG, "Media With Delayed Focus abandon focus " + requestResults); 578 } 579 mDelayedFocusRequest = null; 580 mMediaWithDelayedFocusListener = null; 581 stopDelayedMediaPlayerLocked(); 582 } 583 } 584 } 585 stopDelayedMediaPlayerLocked()586 private void stopDelayedMediaPlayerLocked() { 587 mDelayedStatusText.setText(R.string.player_not_started); 588 resetDeviceSelectedForDelayedMedia(); 589 if (mMusicPlayerWithDelayedFocus.isPlaying()) { 590 if (DBG) Log.d(TAG, "Media With Delayed Focus stopping player"); 591 mMusicPlayerWithDelayedFocus.stop(); 592 return; 593 } 594 if (DBG) Log.d(TAG, "Media With Delayed Focus already stopped"); 595 } 596 pauseDelayedMediaPlayerLocked()597 private void pauseDelayedMediaPlayerLocked() { 598 mDelayedStatusText.setText(R.string.player_paused); 599 resetDeviceSelectedForDelayedMedia(); 600 if (mMusicPlayerWithDelayedFocus.isPlaying()) { 601 if (DBG) Log.d(TAG, "Media With Delayed Focus pausing player"); 602 mMusicPlayerWithDelayedFocus.stop(); 603 return; 604 } 605 if (DBG) Log.d(TAG, "Media With Delayed Focus already stopped"); 606 } 607 setUpDeviceAddressLayoutView(View view)608 private void setUpDeviceAddressLayoutView(View view) { 609 mDeviceAddressLayout = view.findViewById(R.id.audio_select_device_address_layout); 610 611 mDeviceAddressSpinner = view.findViewById(R.id.device_address_spinner); 612 mDeviceAddressSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 613 @Override 614 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 615 handleDeviceAddressSelection(); 616 } 617 618 @Override 619 public void onNothingSelected(AdapterView<?> parent) { 620 } 621 }); 622 } 623 setUpZoneSpinnerView(View view)624 private void setUpZoneSpinnerView(View view) { 625 mZoneSpinner = view.findViewById(R.id.zone_spinner); 626 mZoneSpinner.setEnabled(false); 627 mZoneSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 628 @Override 629 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 630 handleZoneSelection(); 631 } 632 633 @Override 634 public void onNothingSelected(AdapterView<?> parent) { 635 } 636 }); 637 } 638 handleZoneSelection()639 public void handleZoneSelection() { 640 int position = mZoneSpinner.getSelectedItemPosition(); 641 int zone = mZoneAdapter.getItem(position); 642 if (DBG) { 643 Log.d(TAG, "Zone Selected: " + zone); 644 } 645 if (IS_EMULATOR && zone != PRIMARY_AUDIO_ZONE) { 646 setZoneToPlayOnSpeaker(zone); 647 } 648 } 649 handleSetUpZoneSelection()650 private void handleSetUpZoneSelection() { 651 if (!IS_EMULATOR || !mCarAudioManager.isAudioFeatureEnabled( 652 AUDIO_FEATURE_DYNAMIC_ROUTING)) { 653 return; 654 } 655 mZoneSpinner.setEnabled(true); 656 List<Integer> zoneList = mCarAudioManager.getAudioZoneIds(); 657 Integer[] zoneArray = zoneList.stream() 658 .filter(i -> i != PRIMARY_AUDIO_ZONE).toArray(Integer[]::new); 659 mZoneAdapter = new ArrayAdapter<>(mContext, 660 simple_spinner_item, zoneArray); 661 mZoneAdapter.setDropDownViewResource( 662 simple_spinner_dropdown_item); 663 mZoneSpinner.setAdapter(mZoneAdapter); 664 mZoneSpinner.setEnabled(true); 665 } 666 setUpZoneConfigurationSpinnerView(View view)667 private void setUpZoneConfigurationSpinnerView(View view) { 668 mZoneConfigurationLayout = view.findViewById(R.id.audio_zone_configuration_layout); 669 synchronized (mLock) { 670 mCurrentZoneConfigurationView = view.findViewById(R.id.text_current_configuration); 671 } 672 mZoneConfigurationSpinner = view.findViewById(R.id.zone_configuration_spinner); 673 mZoneConfigurationSpinner.setEnabled(false); 674 mZoneConfigurationSpinner.setOnItemSelectedListener( 675 new AdapterView.OnItemSelectedListener() { 676 @Override 677 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 678 handleZoneConfigurationsSelection(); 679 } 680 681 @Override 682 public void onNothingSelected(AdapterView<?> parent) { 683 } 684 }); 685 } 686 handleZoneConfigurationsSelection()687 private void handleZoneConfigurationsSelection() { 688 int position = mZoneConfigurationSpinner.getSelectedItemPosition(); 689 synchronized (mLock) { 690 mZoneConfigInfoSelected = 691 mZoneConfigurationAdapter.getItem(position).getZoneConfigInfo(); 692 } 693 } 694 switchToZoneConfigSelected()695 private void switchToZoneConfigSelected() { 696 CarAudioZoneConfigInfo zoneConfigInfoSelected; 697 synchronized (mLock) { 698 zoneConfigInfoSelected = mZoneConfigInfoSelected; 699 } 700 if (DBG) { 701 Log.d(TAG, "Switch to zone configuration selected: " + zoneConfigInfoSelected); 702 } 703 mCarAudioManager.switchAudioZoneToConfig(zoneConfigInfoSelected, 704 ContextCompat.getMainExecutor(getActivity().getApplicationContext()), 705 mSwitchAudioZoneConfigCallback); 706 } 707 handleSetUpZoneConfigurationSelection()708 private void handleSetUpZoneConfigurationSelection() { 709 if (!mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) { 710 mZoneConfigurationLayout.setVisibility(View.GONE); 711 return; 712 } 713 mZoneConfigurationLayout.setVisibility(View.VISIBLE); 714 int zoneId; 715 try { 716 zoneId = getCurrentZoneId(); 717 } catch (PackageManager.NameNotFoundException e) { 718 Log.e(TAG, "handleSetUpZoneConfigurationSelection failed to find name", e); 719 return; 720 } 721 List<CarAudioZoneConfigInfo> zoneConfigInfos = 722 mCarAudioManager.getAudioZoneConfigInfos(zoneId); 723 CarAudioZoneConfigInfo currentZoneConfigInfo = 724 mCarAudioManager.getCurrentAudioZoneConfigInfo(zoneId); 725 synchronized (mLock) { 726 mCurrentZoneConfigurationView.setText(currentZoneConfigInfo.getName()); 727 mZoneConfigInfoSelected = currentZoneConfigInfo; 728 } 729 CarAudioZoneConfigInfoWrapper[] zoneConfigArray = 730 new CarAudioZoneConfigInfoWrapper[zoneConfigInfos.size()]; 731 for (int index = 0; index < zoneConfigArray.length; index++) { 732 zoneConfigArray[index] = 733 new CarAudioZoneConfigInfoWrapper(zoneConfigInfos.get(index)); 734 } 735 mZoneConfigurationAdapter = new ArrayAdapter<>(mContext, simple_spinner_item, 736 zoneConfigArray); 737 mZoneConfigurationAdapter.setDropDownViewResource( 738 simple_spinner_dropdown_item); 739 mZoneConfigurationSpinner.setAdapter(mZoneConfigurationAdapter); 740 mZoneConfigurationSpinner.setEnabled(true); 741 } 742 setUpTrackToneSpinnerView(View view)743 private void setUpTrackToneSpinnerView(View view) { 744 Spinner toneSpinner = view.findViewById(R.id.tone_spinner); 745 toneSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 746 @Override 747 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 748 handleTrackToneSelection(pos); 749 } 750 751 @Override 752 public void onNothingSelected(AdapterView<?> parent) { 753 } 754 }); 755 ArrayAdapter<Integer> toneAdaper = new ArrayAdapter<Integer>(mContext, 756 simple_spinner_item, TONES); 757 toneAdaper.setDropDownViewResource( 758 simple_spinner_dropdown_item); 759 toneSpinner.setAdapter(toneAdaper); 760 } 761 handleTrackToneSelection(int pos)762 private void handleTrackToneSelection(int pos) { 763 boolean isPlaying = false; 764 int tone = getTone(pos); 765 synchronized (mLock) { 766 if (mAudioTrackPlayer.isPlaying()) { 767 if (DBG) { 768 Log.d(TAG, 769 "Audio Track currently playing, stopping. Switching to frequency: " 770 + tone); 771 } 772 isPlaying = true; 773 mAudioTrackPlayer.stop(); 774 } 775 mAudioTrackPlayer = new AudioTrackPlayer(tone); 776 777 if (isPlaying) { 778 if (DBG) { 779 Log.d(TAG, 780 "Audio Track was playing, starting again. Switched to frequency: " 781 + tone); 782 } 783 mAudioTrackPlayer.start(); 784 } 785 } 786 } 787 getTone(int index)788 private static int getTone(int index) { 789 Preconditions.checkArgumentInRange(index, 0, TONES.length, "index"); 790 return TONES[index]; 791 } 792 setUpCarSoundsLayouts(View view)793 private void setUpCarSoundsLayouts(View view) { 794 mPlayerTabLayout = view.findViewById(R.id.audio_player_tabs); 795 mViewPager = view.findViewById(R.id.zones_player_view_pager); 796 mAudioPlayerZoneAdapter = new CarAudioZoneTabAdapter(getChildFragmentManager()); 797 mViewPager.setAdapter(mAudioPlayerZoneAdapter); 798 mAudioPlayerZoneAdapter.addFragment( 799 new AudioSystemPlayerFragment(mCarAudioManager, mAudioManager), 800 "System Players"); 801 mAudioPlayerZoneAdapter.addFragment(new AudioTransientPlayersFragment(), 802 "Transient Sound Players"); 803 mAudioPlayerZoneAdapter.notifyDataSetChanged(); 804 mPlayerTabLayout.setupWithViewPager(mViewPager); 805 } 806 handleNavStart()807 private void handleNavStart() { 808 if (mAppFocusManager == null) { 809 Log.e(TAG, "mAppFocusManager is null"); 810 return; 811 } 812 if (DBG) { 813 Log.i(TAG, "Nav start"); 814 } 815 mAppFocusManager.requestAppFocus(APP_FOCUS_TYPE_NAVIGATION, mOwnershipCallbacks); 816 mAudioManager.requestAudioFocus(mNavFocusListener, mNavAudioAttrib, 817 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0); 818 } 819 handleNavEnd()820 private void handleNavEnd() { 821 if (mAppFocusManager == null) { 822 Log.e(TAG, "mAppFocusManager is null"); 823 return; 824 } 825 if (DBG) { 826 Log.i(TAG, "Nav end"); 827 } 828 mAudioManager.abandonAudioFocus(mNavFocusListener, mNavAudioAttrib); 829 mAppFocusManager.abandonAppFocus(mOwnershipCallbacks, APP_FOCUS_TYPE_NAVIGATION); 830 } 831 findTunerDevice(Context context)832 private AudioDeviceInfo findTunerDevice(Context context) { 833 AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 834 AudioDeviceInfo[] devices = am.getDevices(GET_DEVICES_INPUTS); 835 for (AudioDeviceInfo device : devices) { 836 if (device.getType() == AudioDeviceInfo.TYPE_FM_TUNER) { 837 return device; 838 } 839 } 840 return null; 841 } 842 handleHwAudioSourceStart()843 private void handleHwAudioSourceStart() { 844 if (mHwAudioSource != null && !mHwAudioSource.isPlaying()) { 845 mHwAudioSource.start(); 846 } 847 } 848 handleHwAudioSourceStop()849 private void handleHwAudioSourceStop() { 850 if (mHwAudioSource != null) { 851 mHwAudioSource.stop(); 852 } 853 } 854 handleVrStart()855 private void handleVrStart() { 856 if (mAppFocusManager == null) { 857 Log.e(TAG, "mAppFocusManager is null"); 858 return; 859 } 860 if (DBG) { 861 Log.i(TAG, "VR start"); 862 } 863 mAppFocusManager.requestAppFocus(APP_FOCUS_TYPE_VOICE_COMMAND, mOwnershipCallbacks); 864 mAudioManager.requestAudioFocus(mVrFocusListener, mVrAudioAttrib, 865 AUDIOFOCUS_GAIN_TRANSIENT, 0); 866 } 867 handleVrEnd()868 private void handleVrEnd() { 869 if (mAppFocusManager == null) { 870 Log.e(TAG, "mAppFocusManager is null"); 871 return; 872 } 873 if (DBG) { 874 Log.i(TAG, "VR end"); 875 } 876 mAudioManager.abandonAudioFocus(mVrFocusListener, mVrAudioAttrib); 877 mAppFocusManager.abandonAppFocus(mOwnershipCallbacks, APP_FOCUS_TYPE_VOICE_COMMAND); 878 } 879 handleRadioStart()880 private void handleRadioStart() { 881 if (DBG) { 882 Log.i(TAG, "Radio start"); 883 } 884 mAudioManager.requestAudioFocus(mRadioFocusListener, mRadioAudioAttrib, AUDIOFOCUS_GAIN, 0); 885 } 886 handleRadioEnd()887 private void handleRadioEnd() { 888 if (DBG) { 889 Log.i(TAG, "Radio end"); 890 } 891 mAudioManager.abandonAudioFocus(mRadioFocusListener, mRadioAudioAttrib); 892 } 893 getCurrentZoneId()894 private int getCurrentZoneId() throws PackageManager.NameNotFoundException { 895 ApplicationInfo info = mContext.getPackageManager().getApplicationInfo( 896 mContext.getPackageName(), 0); 897 return mCarAudioManager.getZoneIdForUid(info.uid); 898 } 899 setUpDeviceAddressPlayer()900 private void setUpDeviceAddressPlayer() { 901 if (!mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) { 902 mDeviceAddressLayout.setVisibility(View.GONE); 903 return; 904 } 905 mDeviceAddressLayout.setVisibility(View.VISIBLE); 906 updateDeviceAddressPlayer(); 907 } 908 updateDeviceAddressPlayer()909 private void updateDeviceAddressPlayer() { 910 List<CarAudioZoneDeviceInfo> deviceList = new ArrayList<>(); 911 for (int audioZoneId: mCarAudioManager.getAudioZoneIds()) { 912 AudioDeviceInfo deviceInfo = mCarAudioManager 913 .getOutputDeviceForUsage(audioZoneId, USAGE_MEDIA); 914 CarAudioZoneDeviceInfo carAudioZoneDeviceInfo = new CarAudioZoneDeviceInfo(); 915 carAudioZoneDeviceInfo.mDeviceInfo = deviceInfo; 916 carAudioZoneDeviceInfo.mAudioZoneId = audioZoneId; 917 deviceList.add(carAudioZoneDeviceInfo); 918 if (DBG) { 919 Log.d(TAG, "Found device address" 920 + carAudioZoneDeviceInfo.mDeviceInfo.getAddress() 921 + " for audio zone id " + audioZoneId); 922 } 923 924 } 925 926 CarAudioZoneDeviceInfo[] deviceArray = 927 deviceList.stream().toArray(CarAudioZoneDeviceInfo[]::new); 928 mDeviceAddressAdapter = new ArrayAdapter<>(mContext, 929 simple_spinner_item, deviceArray); 930 mDeviceAddressAdapter.setDropDownViewResource( 931 simple_spinner_dropdown_item); 932 mDeviceAddressSpinner.setAdapter(mDeviceAddressAdapter); 933 handleDeviceAddressSelection(); 934 } 935 createDeviceAddressAudioPlayer()936 private void createDeviceAddressAudioPlayer() { 937 CarAudioZoneDeviceInfo carAudioZoneDeviceInfo = mDeviceAddressAdapter.getItem( 938 mDeviceAddressSpinner.getSelectedItemPosition()); 939 Log.d(TAG, "Setting Bundle to zone " + carAudioZoneDeviceInfo.mAudioZoneId); 940 Bundle bundle = new Bundle(); 941 bundle.putInt(AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID, carAudioZoneDeviceInfo.mAudioZoneId); 942 mMusicAudioAttribForDeviceAddress = new AudioAttributes.Builder() 943 .setUsage(USAGE_MEDIA) 944 .addBundle(bundle) 945 .build(); 946 947 mMusicPlayerForSelectedDeviceAddress = new AudioPlayer(mContext, well_worth_the_wait, 948 mMusicAudioAttribForDeviceAddress, carAudioZoneDeviceInfo.mDeviceInfo, 949 /* routingListener= */ null); 950 } 951 startDeviceAudio()952 private void startDeviceAudio() { 953 Log.d(TAG, "Starting device address audio"); 954 mMusicPlayerForSelectedDeviceAddress.start(true, false, 955 AUDIOFOCUS_GAIN_TRANSIENT); 956 } 957 handleDeviceAddressSelection()958 public void handleDeviceAddressSelection() { 959 if (mMusicPlayerForSelectedDeviceAddress != null 960 && mMusicPlayerForSelectedDeviceAddress.isPlaying()) { 961 mMusicPlayerForSelectedDeviceAddress.stop(); 962 } 963 createDeviceAddressAudioPlayer(); 964 } 965 966 /** 967 * Sets the left speaker to output sound from zoneId 968 * @param zoneId zone id to set left speakers output 969 * @Note this should only be used with emulator where the zones are separated into right 970 * and left speaker, other platforms would have real devices where audio is routed. 971 */ setZoneToPlayOnSpeaker(int zoneId)972 private void setZoneToPlayOnSpeaker(int zoneId) { 973 String selectedZoneKeyValueString = AAE_PARAMETER_KEY_FOR_SELECTED_ZONE + "=" + zoneId; 974 // send key value parameter list to audio HAL 975 mAudioManager.setParameters(selectedZoneKeyValueString); 976 Log.d(TAG, "setZoneToPlayOnSpeaker : " + zoneId); 977 } 978 resetDeviceSelectedForDelayedMedia()979 private void resetDeviceSelectedForDelayedMedia() { 980 mDelayedAudioDeviceText.setText(getString(R.string.audio_device_selected, 981 DEVICE_SELECTED_NONE)); 982 } 983 984 private class FocusHandler { 985 private static final String AUDIO_FOCUS_STATE_GAIN = "gain"; 986 private static final String AUDIO_FOCUS_STATE_RELEASED_UNKNOWN = "released / unknown"; 987 988 private final RadioGroup mRequestSelection; 989 private final TextView mText; 990 private final AudioFocusListener mFocusListener; 991 private AudioFocusRequest mFocusRequest; 992 FocusHandler(RadioGroup radioGroup, Button requestButton, TextView text)993 public FocusHandler(RadioGroup radioGroup, Button requestButton, TextView text) { 994 mText = text; 995 mRequestSelection = radioGroup; 996 mRequestSelection.check(R.id.focus_gain); 997 setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN); 998 mFocusListener = new AudioFocusListener(); 999 requestButton.setOnClickListener(v -> { 1000 int selectedButtonId = mRequestSelection.getCheckedRadioButtonId(); 1001 int focusRequest; 1002 switch (selectedButtonId) { 1003 case R.id.focus_gain: 1004 focusRequest = AUDIOFOCUS_GAIN; 1005 break; 1006 case R.id.focus_gain_transient: 1007 focusRequest = AUDIOFOCUS_GAIN_TRANSIENT; 1008 break; 1009 case R.id.focus_gain_transient_duck: 1010 focusRequest = AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; 1011 break; 1012 case R.id.focus_gain_transient_exclusive: 1013 focusRequest = AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE; 1014 break; 1015 case R.id.focus_release: 1016 default: 1017 abandonAudioFocus(); 1018 return; 1019 } 1020 mFocusRequest = new AudioFocusRequest.Builder(focusRequest) 1021 .setAudioAttributes(new AudioAttributes.Builder() 1022 .setUsage(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE) 1023 .build()) 1024 .setOnAudioFocusChangeListener(mFocusListener) 1025 .build(); 1026 int ret = mAudioManager.requestAudioFocus(mFocusRequest); 1027 Log.i(TAG, "requestAudioFocus returned " + ret); 1028 if (ret == AUDIOFOCUS_REQUEST_GRANTED) { 1029 setFocusText(AUDIO_FOCUS_STATE_GAIN); 1030 } 1031 }); 1032 } 1033 release()1034 public void release() { 1035 abandonAudioFocus(); 1036 } 1037 abandonAudioFocus()1038 private void abandonAudioFocus() { 1039 if (DBG) { 1040 Log.i(TAG, "abandonAudioFocus"); 1041 } 1042 if (mFocusRequest != null) { 1043 mAudioManager.abandonAudioFocusRequest(mFocusRequest); 1044 mFocusRequest = null; 1045 } else { 1046 Log.i(TAG, "mFocusRequest is already null"); 1047 } 1048 setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN); 1049 } 1050 setFocusText(String msg)1051 private void setFocusText(String msg) { 1052 mText.setText("focus state:" + msg); 1053 } 1054 1055 private class AudioFocusListener implements OnAudioFocusChangeListener { 1056 @Override onAudioFocusChange(int focusChange)1057 public void onAudioFocusChange(int focusChange) { 1058 Log.i(TAG, "onAudioFocusChange " + focusChange); 1059 if (focusChange == AUDIOFOCUS_GAIN) { 1060 setFocusText(AUDIO_FOCUS_STATE_GAIN); 1061 } else if (focusChange == AUDIOFOCUS_LOSS) { 1062 setFocusText("loss"); 1063 } else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT) { 1064 setFocusText("loss,transient"); 1065 } else if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) { 1066 setFocusText("loss,transient,duck"); 1067 } 1068 } 1069 } 1070 } 1071 1072 private final class MediaWithDelayedFocusListener implements OnAudioFocusChangeListener { 1073 @Override onAudioFocusChange(int focusChange)1074 public void onAudioFocusChange(int focusChange) { 1075 if (DBG) Log.d(TAG, "Media With Delayed Focus focus change:" + focusChange); 1076 synchronized (mLock) { 1077 switch (focusChange) { 1078 case AUDIOFOCUS_GAIN: 1079 case AUDIOFOCUS_GAIN_TRANSIENT: 1080 case AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: 1081 case AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: 1082 startDelayedMediaPlayerLocked(); 1083 break; 1084 case AUDIOFOCUS_LOSS_TRANSIENT: 1085 case AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 1086 pauseDelayedMediaPlayerLocked(); 1087 break; 1088 case AUDIOFOCUS_LOSS: 1089 default: 1090 stopDelayedMediaPlayerLocked(); 1091 mDelayedFocusRequest = null; 1092 mMediaWithDelayedFocusListener = null; 1093 break; 1094 } 1095 } 1096 } 1097 } 1098 1099 private static final class CarAudioZoneDeviceInfo { 1100 AudioDeviceInfo mDeviceInfo; 1101 int mAudioZoneId; 1102 1103 @Override toString()1104 public String toString() { 1105 StringBuilder builder = new StringBuilder(); 1106 builder.append("Device Address : "); 1107 builder.append(mDeviceInfo.getAddress()); 1108 builder.append(", Audio Zone Id: "); 1109 builder.append(mAudioZoneId); 1110 return builder.toString(); 1111 } 1112 } 1113 1114 private static final class CarAudioZoneConfigInfoWrapper { 1115 private final CarAudioZoneConfigInfo mZoneConfigInfo; 1116 CarAudioZoneConfigInfoWrapper(CarAudioZoneConfigInfo configInfo)1117 CarAudioZoneConfigInfoWrapper(CarAudioZoneConfigInfo configInfo) { 1118 mZoneConfigInfo = configInfo; 1119 } 1120 getZoneConfigInfo()1121 CarAudioZoneConfigInfo getZoneConfigInfo() { 1122 return mZoneConfigInfo; 1123 } 1124 1125 @Override toString()1126 public String toString() { 1127 StringBuilder builder = new StringBuilder(); 1128 builder.append(mZoneConfigInfo.getName()); 1129 builder.append(", Id: "); 1130 builder.append(mZoneConfigInfo.getConfigId()); 1131 return builder.toString(); 1132 } 1133 } 1134 getAudioLogTag(Class clazz)1135 static String getAudioLogTag(Class clazz) { 1136 return TAG + "." + clazz.getSimpleName(); 1137 } 1138 } 1139