1 /* 2 * Copyright 2017 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.mobileer.oboetester; 18 19 import android.content.Context; 20 import android.media.AudioManager; 21 import android.util.AttributeSet; 22 import android.view.LayoutInflater; 23 import android.view.View; 24 import android.widget.AdapterView; 25 import android.widget.CheckBox; 26 import android.widget.Spinner; 27 import android.widget.TableRow; 28 import android.widget.TextView; 29 import android.widget.LinearLayout; 30 31 import com.mobileer.audio_device.AudioDeviceListEntry; 32 import com.mobileer.audio_device.AudioDeviceSpinner; 33 34 /** 35 * View for Editing a requested StreamConfiguration 36 * and displaying the actual StreamConfiguration. 37 */ 38 39 public class StreamConfigurationView extends LinearLayout { 40 41 private StreamConfiguration mRequestedConfiguration; 42 private StreamConfiguration mActualConfiguration; 43 44 protected Spinner mNativeApiSpinner; 45 private TextView mActualNativeApiView; 46 47 private TextView mActualMMapView; 48 private CheckBox mRequestedMMapView; 49 private TextView mActualExclusiveView; 50 private TextView mActualPerformanceView; 51 private Spinner mPerformanceSpinner; 52 private CheckBox mRequestedExclusiveView; 53 private CheckBox mChannelConversionBox; 54 private CheckBox mFormatConversionBox; 55 private Spinner mChannelCountSpinner; 56 private TextView mActualChannelCountView; 57 private TextView mActualFormatView; 58 59 private TableRow mInputPresetTableRow; 60 private Spinner mInputPresetSpinner; 61 private TextView mActualInputPresetView; 62 63 private TableRow mUsageTableRow; 64 private Spinner mUsageSpinner; 65 private TextView mActualUsageView; 66 67 private Spinner mFormatSpinner; 68 private Spinner mSampleRateSpinner; 69 private Spinner mRateConversionQualitySpinner; 70 private TextView mActualSampleRateView; 71 private LinearLayout mHideableView; 72 73 private AudioDeviceSpinner mDeviceSpinner; 74 private TextView mActualSessionIdView; 75 private CheckBox mRequestAudioEffect; 76 77 private TextView mStreamInfoView; 78 private TextView mStreamStatusView; 79 private TextView mOptionExpander; 80 private String mHideSettingsText; 81 private String mShowSettingsText; 82 83 // Create an anonymous implementation of OnClickListener 84 private View.OnClickListener mToggleListener = new View.OnClickListener() { 85 public void onClick(View v) { 86 if (mHideableView.isShown()) { 87 hideSettingsView(); 88 } else { 89 showSettingsView(); 90 } 91 } 92 }; 93 yesOrNo(boolean b)94 public static String yesOrNo(boolean b) { 95 return b ? "YES" : "NO"; 96 } 97 updateSettingsViewText()98 private void updateSettingsViewText() { 99 if (mHideableView.isShown()) { 100 mOptionExpander.setText(mHideSettingsText); 101 } else { 102 mOptionExpander.setText(mShowSettingsText); 103 } 104 } 105 showSettingsView()106 public void showSettingsView() { 107 mHideableView.setVisibility(View.VISIBLE); 108 updateSettingsViewText(); 109 } 110 hideSampleRateMenu()111 public void hideSampleRateMenu() { 112 if (mSampleRateSpinner != null) { 113 mSampleRateSpinner.setVisibility(View.GONE); 114 } 115 } 116 hideSettingsView()117 public void hideSettingsView() { 118 mHideableView.setVisibility(View.GONE); 119 updateSettingsViewText(); 120 } 121 StreamConfigurationView(Context context)122 public StreamConfigurationView(Context context) { 123 super(context); 124 initializeViews(context); 125 } 126 StreamConfigurationView(Context context, AttributeSet attrs)127 public StreamConfigurationView(Context context, AttributeSet attrs) { 128 super(context, attrs); 129 initializeViews(context); 130 } 131 StreamConfigurationView(Context context, AttributeSet attrs, int defStyle)132 public StreamConfigurationView(Context context, 133 AttributeSet attrs, 134 int defStyle) { 135 super(context, attrs, defStyle); 136 initializeViews(context); 137 } 138 139 /** 140 * Inflates the views in the layout. 141 * 142 * @param context 143 * the current context for the view. 144 */ initializeViews(Context context)145 private void initializeViews(Context context) { 146 LayoutInflater inflater = (LayoutInflater) context 147 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 148 inflater.inflate(R.layout.stream_config, this); 149 150 mHideSettingsText = getResources().getString(R.string.hint_hide_settings); 151 mShowSettingsText = getResources().getString(R.string.hint_show_settings); 152 153 mHideableView = (LinearLayout) findViewById(R.id.hideableView); 154 155 mOptionExpander = (TextView) findViewById(R.id.toggle_stream_config); 156 mOptionExpander.setOnClickListener(mToggleListener); 157 158 mNativeApiSpinner = (Spinner) findViewById(R.id.spinnerNativeApi); 159 mNativeApiSpinner.setOnItemSelectedListener(new NativeApiSpinnerListener()); 160 mNativeApiSpinner.setSelection(StreamConfiguration.NATIVE_API_UNSPECIFIED); 161 162 mActualNativeApiView = (TextView) findViewById(R.id.actualNativeApi); 163 164 mChannelConversionBox = (CheckBox) findViewById(R.id.checkChannelConversion); 165 mChannelConversionBox.setOnClickListener(new View.OnClickListener() { 166 @Override 167 public void onClick(View v) { 168 mRequestedConfiguration.setChannelConversionAllowed(mChannelConversionBox.isChecked()); 169 } 170 }); 171 172 mFormatConversionBox = (CheckBox) findViewById(R.id.checkFormatConversion); 173 mFormatConversionBox.setOnClickListener(new View.OnClickListener() { 174 @Override 175 public void onClick(View v) { 176 mRequestedConfiguration.setFormatConversionAllowed(mFormatConversionBox.isChecked()); 177 } 178 }); 179 180 mActualMMapView = (TextView) findViewById(R.id.actualMMap); 181 mRequestedMMapView = (CheckBox) findViewById(R.id.requestedMMapEnable); 182 mRequestedMMapView.setOnClickListener(new View.OnClickListener() { 183 @Override 184 public void onClick(View v) { 185 mRequestedConfiguration.setMMap(mRequestedMMapView.isChecked()); 186 } 187 }); 188 boolean mmapSupported = NativeEngine.isMMapSupported(); 189 mRequestedMMapView.setEnabled(mmapSupported); 190 mRequestedMMapView.setChecked(mmapSupported); 191 192 mActualExclusiveView = (TextView) findViewById(R.id.actualExclusiveMode); 193 mRequestedExclusiveView = (CheckBox) findViewById(R.id.requestedExclusiveMode); 194 mRequestedExclusiveView.setOnClickListener(new View.OnClickListener() { 195 @Override 196 public void onClick(View v) { 197 mRequestedConfiguration.setSharingMode(mRequestedExclusiveView.isChecked() 198 ? StreamConfiguration.SHARING_MODE_EXCLUSIVE 199 : StreamConfiguration.SHARING_MODE_SHARED); 200 } 201 }); 202 203 boolean mmapExclusiveSupported = NativeEngine.isMMapExclusiveSupported(); 204 mRequestedExclusiveView.setEnabled(mmapExclusiveSupported); 205 mRequestedExclusiveView.setChecked(mmapExclusiveSupported); 206 207 mActualSessionIdView = (TextView) findViewById(R.id.sessionId); 208 mRequestAudioEffect = (CheckBox) findViewById(R.id.requestAudioEffect); 209 mRequestAudioEffect.setOnClickListener(new View.OnClickListener() { 210 @Override 211 public void onClick(View v) { 212 mRequestedConfiguration.setSessionId(mRequestAudioEffect.isChecked() 213 ? StreamConfiguration.SESSION_ID_ALLOCATE 214 : StreamConfiguration.SESSION_ID_NONE); 215 } 216 }); 217 218 mActualSampleRateView = (TextView) findViewById(R.id.actualSampleRate); 219 mSampleRateSpinner = (Spinner) findViewById(R.id.spinnerSampleRate); 220 mSampleRateSpinner.setOnItemSelectedListener(new SampleRateSpinnerListener()); 221 222 mActualChannelCountView = (TextView) findViewById(R.id.actualChannelCount); 223 mChannelCountSpinner = (Spinner) findViewById(R.id.spinnerChannelCount); 224 mChannelCountSpinner.setOnItemSelectedListener(new ChannelCountSpinnerListener()); 225 226 mActualFormatView = (TextView) findViewById(R.id.actualAudioFormat); 227 mFormatSpinner = (Spinner) findViewById(R.id.spinnerFormat); 228 mFormatSpinner.setOnItemSelectedListener(new FormatSpinnerListener()); 229 230 mRateConversionQualitySpinner = (Spinner) findViewById(R.id.spinnerSRCQuality); 231 mRateConversionQualitySpinner.setOnItemSelectedListener(new RateConversionQualitySpinnerListener()); 232 233 mActualPerformanceView = (TextView) findViewById(R.id.actualPerformanceMode); 234 mPerformanceSpinner = (Spinner) findViewById(R.id.spinnerPerformanceMode); 235 mPerformanceSpinner.setOnItemSelectedListener(new PerformanceModeSpinnerListener()); 236 mPerformanceSpinner.setSelection(StreamConfiguration.PERFORMANCE_MODE_LOW_LATENCY 237 - StreamConfiguration.PERFORMANCE_MODE_NONE); 238 239 mInputPresetTableRow = (TableRow) findViewById(R.id.rowInputPreset); 240 mActualInputPresetView = (TextView) findViewById(R.id.actualInputPreset); 241 mInputPresetSpinner = (Spinner) findViewById(R.id.spinnerInputPreset); 242 mInputPresetSpinner.setOnItemSelectedListener(new InputPresetSpinnerListener()); 243 mInputPresetSpinner.setSelection(2); // TODO need better way to select voice recording default 244 245 mUsageTableRow = (TableRow) findViewById(R.id.rowUsage); 246 mActualUsageView = (TextView) findViewById(R.id.actualUsage); 247 mUsageSpinner = (Spinner) findViewById(R.id.spinnerUsage); 248 mUsageSpinner.setOnItemSelectedListener(new UsageSpinnerListener()); 249 mUsageSpinner.setSelection(0); // TODO need better way to select Media default 250 251 mStreamInfoView = (TextView) findViewById(R.id.streamInfo); 252 253 mStreamStatusView = (TextView) findViewById(R.id.statusView); 254 255 mDeviceSpinner = (AudioDeviceSpinner) findViewById(R.id.devices_spinner); 256 mDeviceSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 257 @Override 258 public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { 259 int id = ((AudioDeviceListEntry) mDeviceSpinner.getSelectedItem()).getId(); 260 mRequestedConfiguration.setDeviceId(id); 261 } 262 263 @Override 264 public void onNothingSelected(AdapterView<?> adapterView) { 265 mRequestedConfiguration.setDeviceId(StreamConfiguration.UNSPECIFIED); 266 } 267 }); 268 269 showSettingsView(); 270 } 271 setOutput(boolean output)272 public void setOutput(boolean output) { 273 String ioText; 274 if (output) { 275 mDeviceSpinner.setDirectionType(AudioManager.GET_DEVICES_OUTPUTS); 276 ioText = "OUTPUT"; 277 } else { 278 mDeviceSpinner.setDirectionType(AudioManager.GET_DEVICES_INPUTS); 279 ioText = "INPUT"; 280 } 281 mHideSettingsText = getResources().getString(R.string.hint_hide_settings) + " - " + ioText; 282 mShowSettingsText = getResources().getString(R.string.hint_show_settings) + " - " + ioText; 283 updateSettingsViewText(); 284 285 // Don't show InputPresets for output streams. 286 mInputPresetTableRow.setVisibility(output ? View.GONE : View.VISIBLE); 287 // Don't show Usage for input streams. 288 mUsageTableRow.setVisibility(output ? View.VISIBLE : View.GONE); 289 } 290 291 private class NativeApiSpinnerListener implements android.widget.AdapterView.OnItemSelectedListener { 292 @Override onItemSelected(AdapterView<?> parent, View view, int pos, long id)293 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 294 mRequestedConfiguration.setNativeApi(pos); 295 } 296 297 @Override onNothingSelected(AdapterView<?> parent)298 public void onNothingSelected(AdapterView<?> parent) { 299 mRequestedConfiguration.setNativeApi(StreamConfiguration.NATIVE_API_UNSPECIFIED); 300 } 301 } 302 303 private class PerformanceModeSpinnerListener implements android.widget.AdapterView.OnItemSelectedListener { 304 @Override onItemSelected(AdapterView<?> parent, View view, int performanceMode, long id)305 public void onItemSelected(AdapterView<?> parent, View view, int performanceMode, long id) { 306 mRequestedConfiguration.setPerformanceMode(performanceMode 307 + StreamConfiguration.PERFORMANCE_MODE_NONE); 308 } 309 310 @Override onNothingSelected(AdapterView<?> parent)311 public void onNothingSelected(AdapterView<?> parent) { 312 mRequestedConfiguration.setPerformanceMode(StreamConfiguration.PERFORMANCE_MODE_NONE); 313 } 314 } 315 316 private class ChannelCountSpinnerListener implements android.widget.AdapterView.OnItemSelectedListener { 317 @Override onItemSelected(AdapterView<?> parent, View view, int pos, long id)318 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 319 mRequestedConfiguration.setChannelCount(pos); 320 } 321 322 @Override onNothingSelected(AdapterView<?> parent)323 public void onNothingSelected(AdapterView<?> parent) { 324 mRequestedConfiguration.setChannelCount(StreamConfiguration.UNSPECIFIED); 325 } 326 } 327 328 private class SampleRateSpinnerListener implements android.widget.AdapterView.OnItemSelectedListener { 329 @Override onItemSelected(AdapterView<?> parent, View view, int pos, long id)330 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 331 String text = parent.getItemAtPosition(pos).toString(); 332 int sampleRate = Integer.parseInt(text); 333 mRequestedConfiguration.setSampleRate(sampleRate); 334 } 335 336 @Override onNothingSelected(AdapterView<?> parent)337 public void onNothingSelected(AdapterView<?> parent) { 338 mRequestedConfiguration.setSampleRate(StreamConfiguration.UNSPECIFIED); 339 } 340 } 341 342 private class FormatSpinnerListener implements android.widget.AdapterView.OnItemSelectedListener { 343 @Override onItemSelected(AdapterView<?> parent, View view, int pos, long id)344 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 345 // Menu position matches actual enum value! 346 mRequestedConfiguration.setFormat(pos); 347 } 348 349 @Override onNothingSelected(AdapterView<?> parent)350 public void onNothingSelected(AdapterView<?> parent) { 351 mRequestedConfiguration.setFormat(StreamConfiguration.UNSPECIFIED); 352 } 353 } 354 355 private class InputPresetSpinnerListener implements android.widget.AdapterView.OnItemSelectedListener { 356 @Override onItemSelected(AdapterView<?> parent, View view, int pos, long id)357 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 358 String text = parent.getItemAtPosition(pos).toString(); 359 int inputPreset = StreamConfiguration.convertTextToInputPreset(text); 360 mRequestedConfiguration.setInputPreset(inputPreset); 361 } 362 363 @Override onNothingSelected(AdapterView<?> parent)364 public void onNothingSelected(AdapterView<?> parent) { 365 mRequestedConfiguration.setInputPreset(StreamConfiguration.INPUT_PRESET_GENERIC); 366 } 367 } 368 369 private class UsageSpinnerListener implements android.widget.AdapterView.OnItemSelectedListener { 370 @Override onItemSelected(AdapterView<?> parent, View view, int pos, long id)371 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 372 String text = parent.getItemAtPosition(pos).toString(); 373 int usage = StreamConfiguration.convertTextToUsage(text); 374 mRequestedConfiguration.setUsage(usage); 375 } 376 377 @Override onNothingSelected(AdapterView<?> parent)378 public void onNothingSelected(AdapterView<?> parent) { 379 mRequestedConfiguration.setUsage(StreamConfiguration.INPUT_PRESET_GENERIC); 380 } 381 } 382 383 private class RateConversionQualitySpinnerListener 384 implements android.widget.AdapterView.OnItemSelectedListener { 385 @Override onItemSelected(AdapterView<?> parent, View view, int pos, long id)386 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 387 // Menu position matches actual enum value! 388 mRequestedConfiguration.setRateConversionQuality(pos); 389 } 390 391 @Override onNothingSelected(AdapterView<?> parent)392 public void onNothingSelected(AdapterView<?> parent) { 393 mRequestedConfiguration.setRateConversionQuality(StreamConfiguration.RATE_CONVERSION_QUALITY_HIGH); 394 } 395 } 396 setChildrenEnabled(boolean enabled)397 public void setChildrenEnabled(boolean enabled) { 398 mNativeApiSpinner.setEnabled(enabled); 399 mRequestedMMapView.setEnabled(enabled); 400 mPerformanceSpinner.setEnabled(enabled); 401 mRequestedExclusiveView.setEnabled(enabled); 402 mChannelConversionBox.setEnabled(enabled); 403 mFormatConversionBox.setEnabled(enabled); 404 mChannelCountSpinner.setEnabled(enabled); 405 mInputPresetSpinner.setEnabled(enabled); 406 mUsageSpinner.setEnabled(enabled); 407 mFormatSpinner.setEnabled(enabled); 408 mSampleRateSpinner.setEnabled(enabled); 409 mRateConversionQualitySpinner.setEnabled(enabled); 410 mDeviceSpinner.setEnabled(enabled); 411 mRequestAudioEffect.setEnabled(enabled); 412 } 413 414 // This must be called on the UI thread. updateDisplay()415 void updateDisplay() { 416 int value; 417 418 value = mActualConfiguration.getNativeApi(); 419 mActualNativeApiView.setText(StreamConfiguration.convertNativeApiToText(value)); 420 421 mActualMMapView.setText(yesOrNo(mActualConfiguration.isMMap())); 422 int sharingMode = mActualConfiguration.getSharingMode(); 423 boolean isExclusive = (sharingMode == StreamConfiguration.SHARING_MODE_EXCLUSIVE); 424 mActualExclusiveView.setText(yesOrNo(isExclusive)); 425 426 value = mActualConfiguration.getPerformanceMode(); 427 mActualPerformanceView.setText(StreamConfiguration.convertPerformanceModeToText(value)); 428 mActualPerformanceView.requestLayout(); 429 430 value = mActualConfiguration.getFormat(); 431 mActualFormatView.setText(StreamConfiguration.convertFormatToText(value)); 432 mActualFormatView.requestLayout(); 433 434 value = mActualConfiguration.getInputPreset(); 435 mActualInputPresetView.setText(StreamConfiguration.convertInputPresetToText(value)); 436 mActualInputPresetView.requestLayout(); 437 438 value = mActualConfiguration.getUsage(); 439 mActualUsageView.setText(StreamConfiguration.convertUsageToText(value)); 440 mActualUsageView.requestLayout(); 441 442 mActualChannelCountView.setText(mActualConfiguration.getChannelCount() + ""); 443 mActualSampleRateView.setText(mActualConfiguration.getSampleRate() + ""); 444 mActualSessionIdView.setText("S#: " + mActualConfiguration.getSessionId()); 445 446 boolean isMMap = mActualConfiguration.isMMap(); 447 mStreamInfoView.setText("burst = " + mActualConfiguration.getFramesPerBurst() 448 + ", capacity = " + mActualConfiguration.getBufferCapacityInFrames() 449 + ", devID = " + mActualConfiguration.getDeviceId() 450 + ", " + (mActualConfiguration.isMMap() ? "MMAP" : "Legacy") 451 + (isMMap ? ", " + StreamConfiguration.convertSharingModeToText(sharingMode) : "") 452 ); 453 454 mHideableView.requestLayout(); 455 } 456 457 // This must be called on the UI thread. setStatusText(String msg)458 public void setStatusText(String msg) { 459 mStreamStatusView.setText(msg); 460 } 461 getRequestedConfiguration()462 protected StreamConfiguration getRequestedConfiguration() { 463 return mRequestedConfiguration; 464 } 465 setRequestedConfiguration(StreamConfiguration configuration)466 public void setRequestedConfiguration(StreamConfiguration configuration) { 467 mRequestedConfiguration = configuration; 468 if (configuration != null) { 469 mRateConversionQualitySpinner.setSelection(configuration.getRateConversionQuality()); 470 mChannelConversionBox.setChecked(configuration.getChannelConversionAllowed()); 471 mFormatConversionBox.setChecked(configuration.getFormatConversionAllowed()); 472 } 473 } 474 getActualConfiguration()475 protected StreamConfiguration getActualConfiguration() { 476 return mActualConfiguration; 477 } setActualConfiguration(StreamConfiguration configuration)478 public void setActualConfiguration(StreamConfiguration configuration) { 479 mActualConfiguration = configuration; 480 } 481 setExclusiveMode(boolean b)482 public void setExclusiveMode(boolean b) { 483 mRequestedExclusiveView.setChecked(b); 484 mRequestedConfiguration.setSharingMode(b 485 ? StreamConfiguration.SHARING_MODE_EXCLUSIVE 486 : StreamConfiguration.SHARING_MODE_SHARED); 487 } 488 setFormat(int format)489 public void setFormat(int format) { 490 mFormatSpinner.setSelection(format); // position matches format 491 mRequestedConfiguration.setFormat(format); 492 } 493 setFormatConversionAllowed(boolean allowed)494 public void setFormatConversionAllowed(boolean allowed) { 495 mFormatConversionBox.setChecked(allowed); 496 mRequestedConfiguration.setFormatConversionAllowed(allowed); 497 } 498 } 499