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 org.drrickorang.loopback; 18 19 import android.app.Activity; 20 import android.content.Intent; 21 import android.os.Bundle; 22 import android.util.Log; 23 import android.view.Gravity; 24 import android.view.View; 25 import android.view.ViewGroup; 26 import android.widget.AdapterView; 27 import android.widget.AdapterView.OnItemSelectedListener; 28 import android.widget.ArrayAdapter; 29 import android.widget.CompoundButton; 30 import android.widget.PopupWindow; 31 import android.widget.Spinner; 32 import android.widget.TextView; 33 import android.widget.ToggleButton; 34 35 36 /** 37 * This activity displays all settings that can be adjusted by the user. 38 */ 39 40 public class SettingsActivity extends Activity implements OnItemSelectedListener, 41 ToggleButton.OnCheckedChangeListener { 42 43 private static final String TAG = "SettingsActivity"; 44 45 private Spinner mSpinnerMicSource; 46 private Spinner mSpinnerPerformanceMode; 47 private Spinner mSpinnerSamplingRate; 48 private Spinner mSpinnerAudioThreadType; 49 private TextView mTextSettingsInfo; 50 private Spinner mSpinnerChannelIndex; 51 private SettingsPicker mPlayerBufferUI; 52 private SettingsPicker mRecorderBufferUI; 53 private SettingsPicker mBufferTestDurationUI; 54 private SettingsPicker mWavePlotDurationUI; 55 private SettingsPicker mLoadThreadUI; 56 private SettingsPicker mNumCapturesUI; 57 private SettingsPicker mIgnoreFirstFramesUI; 58 private ToggleButton mSystraceToggleButton; 59 private ToggleButton mBugreportToggleButton; 60 private ToggleButton mWavCaptureToggleButton; 61 private ToggleButton mSoundLevelCalibrationToggleButton; 62 63 ArrayAdapter<CharSequence> mAdapterSamplingRate; 64 65 66 @Override 67 public void onCreate(Bundle savedInstanceState) { 68 super.onCreate(savedInstanceState); 69 // Set the layout for this activity. You can find it 70 View view = getLayoutInflater().inflate(R.layout.settings_activity, null); 71 setContentView(view); 72 mTextSettingsInfo = (TextView) findViewById(R.id.textSettingsInfo); 73 74 int micSource = getApp().getMicSource(); 75 mSpinnerMicSource = (Spinner) findViewById(R.id.spinnerMicSource); 76 ArrayAdapter<CharSequence> adapterMicSource = ArrayAdapter.createFromResource(this, 77 R.array.mic_source_array, android.R.layout.simple_spinner_item); 78 // Specify the layout to use when the list of choices appears 79 adapterMicSource.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 80 // Apply the adapter to the spinner 81 mSpinnerMicSource.setAdapter(adapterMicSource); 82 //set current value 83 mSpinnerMicSource.setSelection(micSource, false); 84 mSpinnerMicSource.setOnItemSelectedListener(this); 85 86 int performanceMode = getApp().getPerformanceMode(); 87 mSpinnerPerformanceMode = (Spinner) findViewById(R.id.spinnerPerformanceMode); 88 ArrayAdapter<CharSequence> adapterPerformanceMode = ArrayAdapter.createFromResource(this, 89 R.array.performance_mode_array, android.R.layout.simple_spinner_item); 90 // Specify the layout to use when the list of choices appears 91 adapterPerformanceMode.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 92 // Apply the adapter to the spinner 93 mSpinnerPerformanceMode.setAdapter(adapterPerformanceMode); 94 //set current value 95 mSpinnerPerformanceMode.setSelection(performanceMode + 1, false); 96 mSpinnerPerformanceMode.setOnItemSelectedListener(this); 97 98 int samplingRate = getApp().getSamplingRate(); 99 //init spinner, etc 100 mSpinnerSamplingRate = (Spinner) findViewById(R.id.spinnerSamplingRate); 101 mAdapterSamplingRate = ArrayAdapter.createFromResource(this, 102 R.array.samplingRate_array, android.R.layout.simple_spinner_item); 103 // Specify the layout to use when the list of choices appears 104 mAdapterSamplingRate.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 105 // Apply the adapter to the spinner 106 mSpinnerSamplingRate.setAdapter(mAdapterSamplingRate); 107 //set current value 108 String currentValue = String.valueOf(samplingRate); 109 int nPosition = mAdapterSamplingRate.getPosition(currentValue); 110 mSpinnerSamplingRate.setSelection(nPosition, false); 111 mSpinnerSamplingRate.setOnItemSelectedListener(this); 112 113 //spinner native 114 int audioThreadType = getApp().getAudioThreadType(); 115 mSpinnerAudioThreadType = (Spinner) findViewById(R.id.spinnerAudioThreadType); 116 ArrayAdapter<CharSequence> adapter2 = ArrayAdapter.createFromResource(this, 117 R.array.audioThreadType_array, android.R.layout.simple_spinner_item); 118 // Specify the layout to use when the list of choices appears 119 adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 120 // Apply the adapter to the spinner 121 mSpinnerAudioThreadType.setAdapter(adapter2); 122 //set current value 123 mSpinnerAudioThreadType.setSelection(audioThreadType, false); 124 if (!getApp().isSafeToUseSles()) 125 mSpinnerAudioThreadType.setEnabled(false); 126 mSpinnerAudioThreadType.setOnItemSelectedListener(this); 127 128 mSpinnerChannelIndex = (Spinner) findViewById(R.id.spinnerChannelIndex); 129 ArrayAdapter<CharSequence> adapter3 = ArrayAdapter.createFromResource(this, 130 R.array.channelIndex_array, android.R.layout.simple_spinner_item); 131 // Specify the layout to use when the list of choices appears 132 adapter3.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 133 // Apply the adapter to the spinner 134 mSpinnerChannelIndex.setAdapter(adapter3); 135 mSpinnerChannelIndex.setOnItemSelectedListener(this); 136 137 // Settings Picker for Buffer Test Duration 138 mBufferTestDurationUI = (SettingsPicker) findViewById(R.id.bufferTestDurationSetting); 139 mBufferTestDurationUI.setMinMaxDefault(Constant.BUFFER_TEST_DURATION_SECONDS_MIN, 140 Constant.BUFFER_TEST_DURATION_SECONDS_MAX, getApp().getBufferTestDuration()); 141 mBufferTestDurationUI.setTitle(getResources().getString(R.string.labelBufferTestDuration, 142 Constant.BUFFER_TEST_DURATION_SECONDS_MAX)); 143 mBufferTestDurationUI.setSettingsChangeListener(new SettingsPicker.SettingChangeListener() { 144 @Override 145 public void settingChanged(int seconds) { 146 log("buffer test new duration: " + seconds); 147 getApp().setBufferTestDuration(seconds); 148 setSettingsHaveChanged(); 149 } 150 }); 151 152 // Settings Picker for Wave Plot Duration 153 mWavePlotDurationUI = (SettingsPicker) findViewById(R.id.wavePlotDurationSetting); 154 mWavePlotDurationUI.setMinMaxDefault(Constant.BUFFER_TEST_WAVE_PLOT_DURATION_SECONDS_MIN, 155 Constant.BUFFER_TEST_WAVE_PLOT_DURATION_SECONDS_MAX, 156 getApp().getBufferTestWavePlotDuration()); 157 mWavePlotDurationUI.setTitle(getResources().getString( 158 R.string.labelBufferTestWavePlotDuration, 159 Constant.BUFFER_TEST_WAVE_PLOT_DURATION_SECONDS_MAX)); 160 mWavePlotDurationUI.setSettingsChangeListener(new SettingsPicker.SettingChangeListener() { 161 @Override 162 public void settingChanged(int value) { 163 log("buffer test's wave plot new duration:" + value); 164 getApp().setBufferTestWavePlotDuration(value); 165 setSettingsHaveChanged(); 166 } 167 }); 168 169 // Settings Picker for Player Buffer Period 170 mPlayerBufferUI = (SettingsPicker) findViewById(R.id.playerBufferSetting); 171 mPlayerBufferUI.setMinMaxDefault(Constant.PLAYER_BUFFER_FRAMES_MIN, 172 Constant.PLAYER_BUFFER_FRAMES_MAX, 173 getApp().getPlayerBufferSizeInBytes() / Constant.BYTES_PER_FRAME); 174 mPlayerBufferUI.setTitle(getResources().getString( 175 R.string.labelPlayerBuffer, Constant.PLAYER_BUFFER_FRAMES_MAX)); 176 mPlayerBufferUI.setSettingsChangeListener(new SettingsPicker.SettingChangeListener() { 177 @Override 178 public void settingChanged(int value) { 179 log("player buffer new size " + value); 180 getApp().setPlayerBufferSizeInBytes(value * Constant.BYTES_PER_FRAME); 181 int audioThreadType = mSpinnerAudioThreadType.getSelectedItemPosition(); 182 // in native mode, recorder buffer size = player buffer size 183 if (audioThreadType == Constant.AUDIO_THREAD_TYPE_NATIVE) { 184 getApp().setRecorderBufferSizeInBytes(value * Constant.BYTES_PER_FRAME); 185 mRecorderBufferUI.setValue(value); 186 } 187 setSettingsHaveChanged(); 188 } 189 }); 190 191 // Settings Picker for Recorder Buffer Period 192 mRecorderBufferUI = (SettingsPicker) findViewById(R.id.recorderBufferSetting); 193 mRecorderBufferUI.setMinMaxDefault(Constant.RECORDER_BUFFER_FRAMES_MIN, 194 Constant.RECORDER_BUFFER_FRAMES_MAX, 195 getApp().getRecorderBufferSizeInBytes() / Constant.BYTES_PER_FRAME); 196 mRecorderBufferUI.setTitle(getResources().getString(R.string.labelRecorderBuffer, 197 Constant.RECORDER_BUFFER_FRAMES_MAX)); 198 mRecorderBufferUI.setSettingsChangeListener(new SettingsPicker.SettingChangeListener() { 199 @Override 200 public void settingChanged(int value) { 201 log("recorder buffer new size:" + value); 202 getApp().setRecorderBufferSizeInBytes(value * Constant.BYTES_PER_FRAME); 203 setSettingsHaveChanged(); 204 } 205 }); 206 207 // Settings Picker for Number of Load Threads 208 mLoadThreadUI = (SettingsPicker) findViewById(R.id.numLoadThreadsSetting); 209 mLoadThreadUI.setMinMaxDefault(Constant.MIN_NUM_LOAD_THREADS, Constant.MAX_NUM_LOAD_THREADS, 210 getApp().getNumberOfLoadThreads()); 211 mLoadThreadUI.setTitle(getResources().getString(R.string.loadThreadsLabel)); 212 mLoadThreadUI.setSettingsChangeListener(new SettingsPicker.SettingChangeListener() { 213 @Override 214 public void settingChanged(int value) { 215 log("new num load threads:" + value); 216 getApp().setNumberOfLoadThreads(value); 217 setSettingsHaveChanged(); 218 } 219 }); 220 221 // Settings Picker for Number of Captures 222 mNumCapturesUI = (SettingsPicker) findViewById(R.id.numCapturesSettingPicker); 223 mNumCapturesUI.setMinMaxDefault(Constant.MIN_NUM_CAPTURES, Constant.MAX_NUM_CAPTURES, 224 getApp().getNumStateCaptures()); 225 mNumCapturesUI.setTitle(getResources().getString(R.string.numCapturesSetting)); 226 mNumCapturesUI.setSettingsChangeListener(new SettingsPicker.SettingChangeListener() { 227 @Override 228 public void settingChanged(int value) { 229 log("new num captures:" + value); 230 getApp().setNumberOfCaptures(value); 231 setSettingsHaveChanged(); 232 } 233 }); 234 235 mWavCaptureToggleButton = (ToggleButton) findViewById(R.id.wavSnippetsEnabledToggle); 236 mWavCaptureToggleButton.setChecked(getApp().isCaptureWavSnippetsEnabled()); 237 mWavCaptureToggleButton.setOnCheckedChangeListener(this); 238 239 mBugreportToggleButton = (ToggleButton) findViewById(R.id.BugreportEnabledToggle); 240 mBugreportToggleButton.setChecked(getApp().isCaptureBugreportEnabled()); 241 mBugreportToggleButton.setOnCheckedChangeListener(this); 242 243 mSystraceToggleButton = (ToggleButton) findViewById(R.id.SystraceEnabledToggle); 244 mSystraceToggleButton.setChecked(getApp().isCaptureSysTraceEnabled()); 245 mSystraceToggleButton.setOnCheckedChangeListener(this); 246 247 mSoundLevelCalibrationToggleButton = (ToggleButton) 248 findViewById(R.id.soundLevelCalibrationEnabledToggle); 249 mSoundLevelCalibrationToggleButton.setChecked(getApp().isSoundLevelCalibrationEnabled()); 250 mSoundLevelCalibrationToggleButton.setOnCheckedChangeListener(this); 251 252 // Settings Picker for number of frames to ignore at the beginning 253 mIgnoreFirstFramesUI = (SettingsPicker) findViewById(R.id.ignoreFirstFramesSettingPicker); 254 mIgnoreFirstFramesUI.setMinMaxDefault(Constant.MIN_IGNORE_FIRST_FRAMES, 255 Constant.MAX_IGNORE_FIRST_FRAMES, getApp().getIgnoreFirstFrames()); 256 mIgnoreFirstFramesUI.setTitle(getResources().getString(R.string.labelIgnoreFirstFrames, 257 Constant.MAX_IGNORE_FIRST_FRAMES)); 258 mIgnoreFirstFramesUI.setSettingsChangeListener(new SettingsPicker.SettingChangeListener() { 259 @Override 260 public void settingChanged(int frames) { 261 log("new number of first frames to ignore: " + frames); 262 getApp().setIgnoreFirstFrames(frames); 263 setSettingsHaveChanged(); 264 } 265 }); 266 267 refresh(); 268 } 269 270 271 public void onDestroy() { 272 super.onDestroy(); 273 } 274 275 276 @Override 277 public void onBackPressed() { 278 log("on back pressed"); 279 setSettingsHaveChanged(); 280 finish(); 281 } 282 283 284 private void refresh() { 285 mBufferTestDurationUI.setValue(getApp().getBufferTestDuration()); 286 mWavePlotDurationUI.setValue(getApp().getBufferTestWavePlotDuration()); 287 288 mPlayerBufferUI.setValue(getApp().getPlayerBufferSizeInBytes() / Constant.BYTES_PER_FRAME); 289 mRecorderBufferUI.setValue( 290 getApp().getRecorderBufferSizeInBytes() / Constant.BYTES_PER_FRAME); 291 292 mRecorderBufferUI.setEnabled( 293 getApp().getAudioThreadType() == Constant.AUDIO_THREAD_TYPE_JAVA); 294 295 int samplingRate = getApp().getSamplingRate(); 296 String currentValue = String.valueOf(samplingRate); 297 int nPosition = mAdapterSamplingRate.getPosition(currentValue); 298 mSpinnerSamplingRate.setSelection(nPosition); 299 300 301 if (getApp().getAudioThreadType() == Constant.AUDIO_THREAD_TYPE_JAVA) { 302 mSpinnerChannelIndex.setSelection(getApp().getChannelIndex() + 1, false); 303 mSpinnerChannelIndex.setEnabled(true); 304 } else { 305 mSpinnerChannelIndex.setSelection(0, false); 306 mSpinnerChannelIndex.setEnabled(false); 307 } 308 309 mNumCapturesUI.setEnabled(getApp().isCaptureEnabled() || 310 getApp().isCaptureWavSnippetsEnabled()); 311 312 String info = getApp().getSystemInfo(); 313 mTextSettingsInfo.setText("SETTINGS - " + info); 314 } 315 316 317 public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { 318 // An item was selected. You can retrieve the selected item using 319 // parent.getItemAtPosition(pos) 320 log("item selected!"); 321 322 switch (parent.getId()) { 323 case R.id.spinnerSamplingRate: 324 String stringValue = mSpinnerSamplingRate.getSelectedItem().toString(); 325 int samplingRate = Integer.parseInt(stringValue); 326 getApp().setSamplingRate(samplingRate); 327 setSettingsHaveChanged(); 328 log("Sampling Rate: " + stringValue); 329 refresh(); 330 break; 331 case R.id.spinnerAudioThreadType: 332 int audioThreadType = mSpinnerAudioThreadType.getSelectedItemPosition(); 333 getApp().setAudioThreadType(audioThreadType); 334 getApp().computeDefaults(); 335 setSettingsHaveChanged(); 336 log("AudioThreadType:" + audioThreadType); 337 refresh(); 338 break; 339 case R.id.spinnerChannelIndex: 340 int channelIndex = mSpinnerChannelIndex.getSelectedItemPosition() - 1; 341 getApp().setChannelIndex(channelIndex); 342 setSettingsHaveChanged(); 343 log("channelIndex:" + channelIndex); 344 refresh(); 345 break; 346 case R.id.spinnerMicSource: 347 int micSource = mSpinnerMicSource.getSelectedItemPosition(); 348 getApp().setMicSource(micSource); 349 setSettingsHaveChanged(); 350 log("mic Source:" + micSource); 351 refresh(); 352 break; 353 case R.id.spinnerPerformanceMode: 354 int performanceMode = mSpinnerPerformanceMode.getSelectedItemPosition() - 1; 355 getApp().setPerformanceMode(performanceMode); 356 setSettingsHaveChanged(); 357 log("performanceMode:" + performanceMode); 358 refresh(); 359 break; 360 } 361 } 362 363 @Override 364 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 365 if (buttonView.getId() == mWavCaptureToggleButton.getId()){ 366 getApp().setCaptureWavsEnabled(isChecked); 367 } else if (buttonView.getId() == mSystraceToggleButton.getId()) { 368 getApp().setCaptureSysTraceEnabled(isChecked); 369 } else if (buttonView.getId() == mBugreportToggleButton.getId()) { 370 getApp().setCaptureBugreportEnabled(isChecked); 371 } else if (buttonView.getId() == mSoundLevelCalibrationToggleButton.getId()) { 372 getApp().setSoundLevelCalibrationEnabled(isChecked); 373 } 374 mNumCapturesUI.setEnabled(getApp().isCaptureEnabled() || 375 getApp().isCaptureWavSnippetsEnabled()); 376 } 377 378 private void setSettingsHaveChanged() { 379 Intent intent = new Intent(); 380 setResult(RESULT_OK, intent); 381 } 382 383 384 public void onNothingSelected(AdapterView<?> parent) { 385 // Another interface callback 386 } 387 388 public void onButtonHelp(View view) { 389 // Create a PopUpWindow with scrollable TextView 390 View puLayout = this.getLayoutInflater().inflate(R.layout.report_window, null); 391 PopupWindow popUp = new PopupWindow(puLayout, ViewGroup.LayoutParams.MATCH_PARENT, 392 ViewGroup.LayoutParams.MATCH_PARENT, true); 393 394 TextView helpText = 395 (TextView) popUp.getContentView().findViewById(R.id.ReportInfo); 396 if (view.getId() == R.id.buttonSystraceHelp || view.getId() == R.id.buttonBugreportHelp) { 397 helpText.setText(getResources().getString(R.string.systraceHelp)); 398 } else if (view.getId() == R.id.buttonCalibrateSoundLevelHelp) { 399 helpText.setText(getResources().getString(R.string.calibrateSoundLevelHelp)); 400 } 401 402 // display pop up window, dismissible with back button 403 popUp.showAtLocation(findViewById(R.id.settingsMainLayout), Gravity.TOP, 0, 0); 404 } 405 406 /** Called when the user clicks the button */ 407 public void onButtonClick(View view) { 408 getApp().computeDefaults(); 409 refresh(); 410 } 411 412 // Below is work in progress by Ricardo 413 // public void onButtonRecordDefault(View view) { 414 // int samplingRate = getApp().getSamplingRate(); 415 // 416 // int minRecorderBufferSizeInBytes = AudioRecord.getMinBufferSize(samplingRate, 417 // AudioFormat.CHANNEL_IN_MONO, 418 // AudioFormat.ENCODING_PCM_16BIT); 419 // getApp().setRecorderBufferSizeInBytes(minRecorderBufferSizeInBytes); 420 // 421 // refresh(); 422 // } 423 424 // private void computeDefaults() { 425 // 426 //// if (getApp().getAudioThreadType() == LoopbackApplication.AUDIO_THREAD_TYPE_JAVA) { 427 //// mNumberPickerRecorderBuffer.setEnabled(true); 428 //// else 429 //// mNumberPickerRecorderBuffer.setEnabled(false); 430 // 431 // int samplingRate = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC); 432 // getApp().setSamplingRate(samplingRate); 433 // int minPlayerBufferSizeInBytes = AudioTrack.getMinBufferSize(samplingRate, 434 // AudioFormat.CHANNEL_OUT_MONO, 435 // AudioFormat.ENCODING_PCM_16BIT); 436 // getApp().setPlayerBufferSizeInBytes(minPlayerBufferSizeInBytes); 437 // 438 // int minRecorderBufferSizeInBytes = AudioRecord.getMinBufferSize(samplingRate, 439 // AudioFormat.CHANNEL_IN_MONO, 440 // AudioFormat.ENCODING_PCM_16BIT); 441 // getApp().setRecorderBufferSizeInBytes(minRecorderBufferSizeInBytes); 442 // getApp().setRecorderBufferSizeInBytes(minRecorderBufferSizeInBytes); 443 // 444 // log("computed defaults"); 445 // 446 // } 447 448 private LoopbackApplication getApp() { 449 return (LoopbackApplication) this.getApplication(); 450 } 451 452 453 private static void log(String msg) { 454 Log.v(TAG, msg); 455 } 456 457 458 } 459