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 17package com.google.android.car.kitchensink.audio; 18 19import android.car.Car; 20import android.car.CarAppContextManager; 21import android.car.CarAppContextManager.AppContextChangeListener; 22import android.car.CarAppContextManager.AppContextOwnershipChangeListener; 23import android.car.CarNotConnectedException; 24import android.car.media.CarAudioManager; 25import android.content.ComponentName; 26import android.content.Context; 27import android.content.ServiceConnection; 28import android.media.AudioAttributes; 29import android.media.AudioManager; 30import android.os.Bundle; 31import android.os.Handler; 32import android.os.IBinder; 33import android.os.Looper; 34import android.support.v4.app.Fragment; 35import android.util.Log; 36import android.view.LayoutInflater; 37import android.view.View; 38import android.view.View.OnClickListener; 39import android.view.ViewGroup; 40import android.widget.Button; 41import android.widget.CompoundButton; 42import android.widget.CompoundButton.OnCheckedChangeListener; 43import android.widget.RadioGroup; 44import android.widget.TextView; 45import android.widget.ToggleButton; 46 47import com.google.android.car.kitchensink.CarEmulator; 48import com.google.android.car.kitchensink.R; 49import com.google.android.car.kitchensink.audio.AudioPlayer.PlayStateListener; 50 51public class AudioTestFragment extends Fragment { 52 private static final String TAG = "CAR.AUDIO.KS"; 53 private static final boolean DBG = true; 54 55 private AudioManager mAudioManager; 56 private FocusHandler mAudioFocusHandler; 57 private Button mNavPlayOnce; 58 private Button mVrPlayOnce; 59 private Button mSystemPlayOnce; 60 private Button mMediaPlay; 61 private Button mMediaPlayOnce; 62 private Button mMediaStop; 63 private Button mNavStart; 64 private Button mNavEnd; 65 private Button mVrStart; 66 private Button mVrEnd; 67 private Button mRadioStart; 68 private Button mRadioEnd; 69 private Button mSpeakerPhoneOn; 70 private Button mSpeakerPhoneOff; 71 private Button mMicrophoneOn; 72 private Button mMicrophoneOff; 73 private ToggleButton mEnableMocking; 74 private ToggleButton mRejectFocus; 75 76 private AudioPlayer mMusicPlayer; 77 private AudioPlayer mMusicPlayerShort; 78 private AudioPlayer mNavGuidancePlayer; 79 private AudioPlayer mVrPlayer; 80 private AudioPlayer mSystemPlayer; 81 private AudioPlayer[] mAllPlayers; 82 83 private Handler mHandler; 84 private Context mContext; 85 86 private Car mCar; 87 private CarAppContextManager mAppContextManager; 88 private CarAudioManager mCarAudioManager; 89 private AudioAttributes mMusicAudioAttrib; 90 private AudioAttributes mNavAudioAttrib; 91 private AudioAttributes mVrAudioAttrib; 92 private AudioAttributes mRadioAudioAttrib; 93 private AudioAttributes mSystemSoundAudioAttrib; 94 private CarEmulator mCarEmulator; 95 96 private final AudioManager.OnAudioFocusChangeListener mNavFocusListener = 97 new AudioManager.OnAudioFocusChangeListener() { 98 @Override 99 public void onAudioFocusChange(int focusChange) { 100 Log.i(TAG, "Nav focus change:" + focusChange); 101 } 102 }; 103 private final AudioManager.OnAudioFocusChangeListener mVrFocusListener = 104 new AudioManager.OnAudioFocusChangeListener() { 105 @Override 106 public void onAudioFocusChange(int focusChange) { 107 Log.i(TAG, "VR focus change:" + focusChange); 108 } 109 }; 110 private final AudioManager.OnAudioFocusChangeListener mRadioFocusListener = 111 new AudioManager.OnAudioFocusChangeListener() { 112 @Override 113 public void onAudioFocusChange(int focusChange) { 114 Log.i(TAG, "Radio focus change:" + focusChange); 115 } 116 }; 117 118 private final AppContextOwnershipChangeListener mOwnershipListener = 119 new AppContextOwnershipChangeListener() { 120 @Override 121 public void onAppContextOwnershipLoss(int context) { 122 } 123 }; 124 125 private void init() { 126 mContext = getContext(); 127 mHandler = new Handler(Looper.getMainLooper()); 128 mCar = Car.createCar(mContext, new ServiceConnection() { 129 @Override 130 public void onServiceConnected(ComponentName name, IBinder service) { 131 try { 132 mAppContextManager = 133 (CarAppContextManager) mCar.getCarManager(Car.APP_CONTEXT_SERVICE); 134 } catch (CarNotConnectedException e) { 135 throw new RuntimeException("Failed to create app context manager", e); 136 } 137 try { 138 mAppContextManager.registerContextListener(new AppContextChangeListener() { 139 @Override 140 public void onAppContextChange(int activeContexts) { 141 } 142 }, CarAppContextManager.APP_CONTEXT_NAVIGATION | 143 CarAppContextManager.APP_CONTEXT_VOICE_COMMAND); 144 } catch (CarNotConnectedException e) { 145 Log.e(TAG, "Failed to register context listener", e); 146 } 147 try { 148 mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE); 149 } catch (CarNotConnectedException e) { 150 throw new RuntimeException("Failed to create audio manager", e); 151 } 152 mMusicAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage( 153 CarAudioManager.CAR_AUDIO_USAGE_MUSIC); 154 mNavAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage( 155 CarAudioManager.CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE); 156 mVrAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage( 157 CarAudioManager.CAR_AUDIO_USAGE_VOICE_COMMAND); 158 mRadioAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage( 159 CarAudioManager.CAR_AUDIO_USAGE_RADIO); 160 mSystemSoundAudioAttrib = mCarAudioManager.getAudioAttributesForCarUsage( 161 CarAudioManager.CAR_AUDIO_USAGE_SYSTEM_SOUND); 162 163 mMusicPlayer = new AudioPlayer(mContext, R.raw.john_harrison_with_the_wichita_state_university_chamber_players_05_summer_mvt_2_adagio, 164 mMusicAudioAttrib); 165 mMusicPlayerShort = new AudioPlayer(mContext, R.raw.ring_classic_01, 166 mMusicAudioAttrib); 167 mNavGuidancePlayer = new AudioPlayer(mContext, R.raw.turnright, 168 mNavAudioAttrib); 169 // no Usage for voice command yet. 170 mVrPlayer = new AudioPlayer(mContext, R.raw.one2six, 171 mVrAudioAttrib); 172 mSystemPlayer = new AudioPlayer(mContext, R.raw.ring_classic_01, 173 mSystemSoundAudioAttrib); 174 mAllPlayers = new AudioPlayer[] { 175 mMusicPlayer, 176 mMusicPlayerShort, 177 mNavGuidancePlayer, 178 mVrPlayer, 179 mSystemPlayer 180 }; 181 } 182 @Override 183 public void onServiceDisconnected(ComponentName name) { 184 } 185 }, Looper.getMainLooper()); 186 mCar.connect(); 187 } 188 189 @Override 190 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) { 191 Log.i(TAG, "onCreateView"); 192 init(); 193 View view = inflater.inflate(R.layout.audio, container, false); 194 mAudioManager = (AudioManager) mContext.getSystemService( 195 Context.AUDIO_SERVICE); 196 mAudioFocusHandler = new FocusHandler( 197 (RadioGroup) view.findViewById(R.id.button_focus_request_selection), 198 (Button) view.findViewById(R.id.button_audio_focus_request), 199 (TextView) view.findViewById(R.id.text_audio_focus_state)); 200 mMediaPlay = (Button) view.findViewById(R.id.button_media_play_start); 201 mMediaPlay.setOnClickListener(new OnClickListener() { 202 @Override 203 public void onClick(View v) { 204 mMusicPlayer.start(false, true, AudioManager.AUDIOFOCUS_GAIN); 205 } 206 }); 207 mMediaPlayOnce = (Button) view.findViewById(R.id.button_media_play_once); 208 mMediaPlayOnce.setOnClickListener(new OnClickListener() { 209 @Override 210 public void onClick(View v) { 211 mMusicPlayerShort.start(true, false, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 212 // play only for 1 sec and stop 213 mHandler.postDelayed(new Runnable() { 214 @Override 215 public void run() { 216 mMusicPlayerShort.stop(); 217 } 218 }, 1000); 219 } 220 }); 221 mMediaStop = (Button) view.findViewById(R.id.button_media_play_stop); 222 mMediaStop.setOnClickListener(new OnClickListener() { 223 @Override 224 public void onClick(View v) { 225 mMusicPlayer.stop(); 226 } 227 }); 228 mNavPlayOnce = (Button) view.findViewById(R.id.button_nav_play_once); 229 mNavPlayOnce.setOnClickListener(new OnClickListener() { 230 @Override 231 public void onClick(View v) { 232 if (mAppContextManager == null) { 233 return; 234 } 235 if (DBG) { 236 Log.i(TAG, "Nav start"); 237 } 238 if (!mNavGuidancePlayer.isPlaying()) { 239 try { 240 mAppContextManager.setActiveContexts(mOwnershipListener, 241 CarAppContextManager.APP_CONTEXT_NAVIGATION); 242 } catch (CarNotConnectedException e) { 243 Log.e(TAG, "Failed to set active context", e); 244 } 245 mNavGuidancePlayer.start(true, false, 246 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 247 new PlayStateListener() { 248 @Override 249 public void onCompletion() { 250 try { 251 mAppContextManager.resetActiveContexts( 252 CarAppContextManager.APP_CONTEXT_NAVIGATION); 253 } catch (CarNotConnectedException e) { 254 Log.e(TAG, "Failed to reset active context", e); 255 } 256 } 257 }); 258 } 259 } 260 }); 261 mVrPlayOnce = (Button) view.findViewById(R.id.button_vr_play_once); 262 mVrPlayOnce.setOnClickListener(new OnClickListener() { 263 @Override 264 public void onClick(View v) { 265 if (mAppContextManager == null) { 266 return; 267 } 268 if (DBG) { 269 Log.i(TAG, "VR start"); 270 } 271 try { 272 mAppContextManager.setActiveContexts(mOwnershipListener, 273 CarAppContextManager.APP_CONTEXT_VOICE_COMMAND); 274 } catch (CarNotConnectedException e) { 275 Log.e(TAG, "Failed to set active context", e); 276 } 277 if (!mVrPlayer.isPlaying()) { 278 mVrPlayer.start(true, false, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, 279 new PlayStateListener() { 280 @Override 281 public void onCompletion() { 282 try { 283 mAppContextManager.resetActiveContexts( 284 CarAppContextManager.APP_CONTEXT_VOICE_COMMAND); 285 } catch (CarNotConnectedException e) { 286 Log.e(TAG, "Failed to reset active context", e); 287 } 288 } 289 }); 290 } 291 } 292 }); 293 mSystemPlayOnce = (Button) view.findViewById(R.id.button_system_play_once); 294 mSystemPlayOnce.setOnClickListener(new OnClickListener() { 295 @Override 296 public void onClick(View v) { 297 if (DBG) { 298 Log.i(TAG, "System start"); 299 } 300 if (!mSystemPlayer.isPlaying()) { 301 // system sound played without focus 302 mSystemPlayer.start(false, false, 0); 303 } 304 } 305 }); 306 mNavStart = (Button) view.findViewById(R.id.button_nav_start); 307 mNavStart.setOnClickListener(new OnClickListener() { 308 @Override 309 public void onClick(View v) { 310 handleNavStart(); 311 } 312 }); 313 mNavEnd = (Button) view.findViewById(R.id.button_nav_end); 314 mNavEnd.setOnClickListener(new OnClickListener() { 315 @Override 316 public void onClick(View v) { 317 handleNavEnd(); 318 } 319 }); 320 mVrStart = (Button) view.findViewById(R.id.button_vr_start); 321 mVrStart.setOnClickListener(new OnClickListener() { 322 @Override 323 public void onClick(View v) { 324 handleVrStart(); 325 } 326 }); 327 mVrEnd = (Button) view.findViewById(R.id.button_vr_end); 328 mVrEnd.setOnClickListener(new OnClickListener() { 329 @Override 330 public void onClick(View v) { 331 handleVrEnd(); 332 } 333 }); 334 mRadioStart = (Button) view.findViewById(R.id.button_radio_start); 335 mRadioStart.setOnClickListener(new OnClickListener() { 336 @Override 337 public void onClick(View v) { 338 handleRadioStart(); 339 } 340 }); 341 mRadioEnd = (Button) view.findViewById(R.id.button_radio_end); 342 mRadioEnd.setOnClickListener(new OnClickListener() { 343 @Override 344 public void onClick(View v) { 345 handleRadioEnd(); 346 } 347 }); 348 mSpeakerPhoneOn = (Button) view.findViewById(R.id.button_speaker_phone_on); 349 mSpeakerPhoneOn.setOnClickListener(new OnClickListener() { 350 @Override 351 public void onClick(View v) { 352 mAudioManager.setSpeakerphoneOn(true); 353 } 354 }); 355 mSpeakerPhoneOff = (Button) view.findViewById(R.id.button_speaker_phone_off); 356 mSpeakerPhoneOff.setOnClickListener(new OnClickListener() { 357 @Override 358 public void onClick(View v) { 359 mAudioManager.setSpeakerphoneOn(false); 360 } 361 }); 362 mMicrophoneOn = (Button) view.findViewById(R.id.button_microphone_on); 363 mMicrophoneOn.setOnClickListener(new OnClickListener() { 364 @Override 365 public void onClick(View v) { 366 mAudioManager.setMicrophoneMute(false); // Turn the microphone on. 367 } 368 }); 369 mMicrophoneOff = (Button) view.findViewById(R.id.button_microphone_off); 370 mMicrophoneOff.setOnClickListener(new OnClickListener() { 371 @Override 372 public void onClick(View v) { 373 mAudioManager.setMicrophoneMute(true); // Mute the microphone. 374 } 375 }); 376 377 378 mRejectFocus = (ToggleButton) view.findViewById(R.id.button_reject_audio_focus); 379 mRejectFocus.setOnCheckedChangeListener(new OnCheckedChangeListener() { 380 @Override 381 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 382 if (mCarEmulator == null) { 383 return; 384 } 385 if (!mEnableMocking.isChecked()) { 386 return; 387 } 388 if (isChecked) { 389 mCarEmulator.setAudioFocusControl(true); 390 } else { 391 mCarEmulator.setAudioFocusControl(false); 392 } 393 } 394 }); 395 mRejectFocus.setActivated(false); 396 mEnableMocking = (ToggleButton) view.findViewById(R.id.button_mock_audio); 397 mEnableMocking.setOnCheckedChangeListener(new OnCheckedChangeListener() { 398 @Override 399 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 400 if (mCarEmulator == null) { 401 mCarEmulator = new CarEmulator(mCar); 402 } 403 if (isChecked) { 404 mRejectFocus.setActivated(true); 405 mCarEmulator.start(); 406 } else { 407 mRejectFocus.setActivated(false); 408 mCarEmulator.stop(); 409 mCarEmulator = null; 410 } 411 } 412 }); 413 return view; 414 } 415 416 @Override 417 public void onDestroyView() { 418 super.onDestroyView(); 419 Log.i(TAG, "onDestroyView"); 420 if (mCarEmulator != null) { 421 mCarEmulator.setAudioFocusControl(false); 422 mCarEmulator.stop(); 423 } 424 for (AudioPlayer p : mAllPlayers) { 425 p.stop(); 426 } 427 if (mAudioFocusHandler != null) { 428 mAudioFocusHandler.release(); 429 mAudioFocusHandler = null; 430 } 431 if (mAppContextManager != null) { 432 try { 433 mAppContextManager.resetActiveContexts(CarAppContextManager.APP_CONTEXT_NAVIGATION | 434 CarAppContextManager.APP_CONTEXT_VOICE_COMMAND); 435 } catch (CarNotConnectedException e) { 436 Log.e(TAG, "Failed to reset active context", e); 437 } 438 } 439 } 440 441 private void handleNavStart() { 442 if (mAppContextManager == null) { 443 return; 444 } 445 if (mCarAudioManager == null) { 446 return; 447 } 448 if (DBG) { 449 Log.i(TAG, "Nav start"); 450 } 451 try { 452 mAppContextManager.setActiveContexts(mOwnershipListener, 453 CarAppContextManager.APP_CONTEXT_NAVIGATION); 454 } catch (CarNotConnectedException e) { 455 Log.e(TAG, "Failed to set active context", e); 456 } 457 mCarAudioManager.requestAudioFocus(mNavFocusListener, mNavAudioAttrib, 458 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, 0); 459 } 460 461 private void handleNavEnd() { 462 if (mAppContextManager == null) { 463 return; 464 } 465 if (mCarAudioManager == null) { 466 return; 467 } 468 if (DBG) { 469 Log.i(TAG, "Nav end"); 470 } 471 try { 472 mAppContextManager.resetActiveContexts( 473 CarAppContextManager.APP_CONTEXT_NAVIGATION); 474 } catch (CarNotConnectedException e) { 475 Log.e(TAG, "Failed to reset active context", e); 476 } 477 mCarAudioManager.abandonAudioFocus(mNavFocusListener, mNavAudioAttrib); 478 } 479 480 private void handleVrStart() { 481 if (mAppContextManager == null) { 482 return; 483 } 484 if (mCarAudioManager == null) { 485 return; 486 } 487 if (DBG) { 488 Log.i(TAG, "VR start"); 489 } 490 try { 491 mAppContextManager.setActiveContexts(mOwnershipListener, 492 CarAppContextManager.APP_CONTEXT_VOICE_COMMAND); 493 } catch (CarNotConnectedException e) { 494 Log.e(TAG, "Failed to set active context", e); 495 } 496 mCarAudioManager.requestAudioFocus(mVrFocusListener, mVrAudioAttrib, 497 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, 0); 498 } 499 500 private void handleVrEnd() { 501 if (mAppContextManager == null) { 502 return; 503 } 504 if (mCarAudioManager == null) { 505 return; 506 } 507 if (DBG) { 508 Log.i(TAG, "VR end"); 509 } 510 try { 511 mAppContextManager.resetActiveContexts( 512 CarAppContextManager.APP_CONTEXT_VOICE_COMMAND); 513 } catch (CarNotConnectedException e) { 514 Log.e(TAG, "Failed to reset active context", e); 515 } 516 mCarAudioManager.abandonAudioFocus(mVrFocusListener, mVrAudioAttrib); 517 } 518 519 private void handleRadioStart() { 520 if (mCarAudioManager == null) { 521 return; 522 } 523 if (DBG) { 524 Log.i(TAG, "Radio start"); 525 } 526 mCarAudioManager.requestAudioFocus(mRadioFocusListener, mRadioAudioAttrib, 527 AudioManager.AUDIOFOCUS_GAIN, 0); 528 } 529 530 private void handleRadioEnd() { 531 if (mCarAudioManager == null) { 532 return; 533 } 534 if (DBG) { 535 Log.i(TAG, "Radio end"); 536 } 537 mCarAudioManager.abandonAudioFocus(mRadioFocusListener, mRadioAudioAttrib); 538 } 539 540 private class FocusHandler { 541 private static final String AUDIO_FOCUS_STATE_GAIN = "gain"; 542 private static final String AUDIO_FOCUS_STATE_RELEASED_UNKNOWN = "released / unknown"; 543 544 private final RadioGroup mRequestSelection; 545 private final TextView mText; 546 private final AudioFocusListener mFocusListener; 547 548 public FocusHandler(RadioGroup radioGroup, Button requestButton, TextView text) { 549 mText = text; 550 mRequestSelection = radioGroup; 551 mRequestSelection.check(R.id.focus_gain); 552 setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN); 553 mFocusListener = new AudioFocusListener(); 554 requestButton.setOnClickListener(new OnClickListener() { 555 @Override 556 public void onClick(View v) { 557 int selectedButtonId = mRequestSelection.getCheckedRadioButtonId(); 558 int focusRequest = AudioManager.AUDIOFOCUS_GAIN; 559 if (selectedButtonId == R.id.focus_gain_transient_duck) { 560 focusRequest = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; 561 } else if (selectedButtonId == R.id.focus_release) { 562 mAudioManager.abandonAudioFocus(mFocusListener); 563 setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN); 564 return; 565 } 566 int ret = mAudioManager.requestAudioFocus(mFocusListener, 567 AudioManager.STREAM_MUSIC, focusRequest); 568 Log.i(TAG, "requestAudioFocus returned " + ret); 569 if (ret == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 570 setFocusText(AUDIO_FOCUS_STATE_GAIN); 571 } 572 } 573 }); 574 } 575 576 public void release() { 577 abandonAudioFocus(); 578 } 579 580 private void abandonAudioFocus() { 581 if (DBG) { 582 Log.i(TAG, "abandonAudioFocus"); 583 } 584 mAudioManager.abandonAudioFocus(mFocusListener); 585 setFocusText(AUDIO_FOCUS_STATE_RELEASED_UNKNOWN); 586 } 587 588 private void setFocusText(String msg) { 589 mText.setText("focus state:" + msg); 590 } 591 592 private class AudioFocusListener implements AudioManager.OnAudioFocusChangeListener { 593 @Override 594 public void onAudioFocusChange(int focusChange) { 595 Log.i(TAG, "onAudioFocusChange " + focusChange); 596 if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { 597 setFocusText(AUDIO_FOCUS_STATE_GAIN); 598 } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { 599 setFocusText("loss"); 600 } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) { 601 setFocusText("loss,transient"); 602 } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) { 603 setFocusText("loss,transient,duck"); 604 } 605 } 606 } 607 } 608} 609