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