1 /* 2 * Copyright 2018 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.oboe.samples.liveEffect; 18 19 import android.Manifest; 20 import android.app.Activity; 21 import android.content.pm.PackageManager; 22 import android.media.AudioManager; 23 import android.os.Build; 24 import android.os.Bundle; 25 import androidx.annotation.NonNull; 26 import androidx.core.app.ActivityCompat; 27 import android.util.Log; 28 import android.view.View; 29 import android.widget.AdapterView; 30 import android.widget.Button; 31 import android.widget.RadioButton; 32 import android.widget.RadioGroup; 33 import android.widget.TextView; 34 import android.widget.Toast; 35 36 import com.google.oboe.samples.audio_device.AudioDeviceListEntry; 37 import com.google.oboe.samples.audio_device.AudioDeviceSpinner; 38 39 /** 40 * TODO: Update README.md and go through and comment sample 41 */ 42 public class MainActivity extends Activity 43 implements ActivityCompat.OnRequestPermissionsResultCallback { 44 45 private static final String TAG = MainActivity.class.getName(); 46 private static final int AUDIO_EFFECT_REQUEST = 0; 47 private static final int OBOE_API_AAUDIO = 0; 48 private static final int OBOE_API_OPENSL_ES=1; 49 50 private TextView statusText; 51 private Button toggleEffectButton; 52 private AudioDeviceSpinner recordingDeviceSpinner; 53 private AudioDeviceSpinner playbackDeviceSpinner; 54 private boolean isPlaying = false; 55 56 private int apiSelection = OBOE_API_AAUDIO; 57 private boolean aaudioSupported = true; 58 59 @Override onCreate(Bundle savedInstanceState)60 protected void onCreate(Bundle savedInstanceState) { 61 super.onCreate(savedInstanceState); 62 setContentView(R.layout.activity_main); 63 64 statusText = findViewById(R.id.status_view_text); 65 toggleEffectButton = findViewById(R.id.button_toggle_effect); 66 toggleEffectButton.setOnClickListener(new View.OnClickListener() { 67 @Override 68 public void onClick(View view) { 69 toggleEffect(); 70 } 71 }); 72 toggleEffectButton.setText(getString(R.string.start_effect)); 73 74 recordingDeviceSpinner = findViewById(R.id.recording_devices_spinner); 75 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 76 recordingDeviceSpinner.setDirectionType(AudioManager.GET_DEVICES_INPUTS); 77 recordingDeviceSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 78 @Override 79 public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { 80 LiveEffectEngine.setRecordingDeviceId(getRecordingDeviceId()); 81 } 82 83 @Override 84 public void onNothingSelected(AdapterView<?> adapterView) { 85 // Do nothing 86 } 87 }); 88 } 89 90 playbackDeviceSpinner = findViewById(R.id.playback_devices_spinner); 91 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 92 playbackDeviceSpinner.setDirectionType(AudioManager.GET_DEVICES_OUTPUTS); 93 playbackDeviceSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 94 @Override 95 public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { 96 LiveEffectEngine.setPlaybackDeviceId(getPlaybackDeviceId()); 97 } 98 99 @Override 100 public void onNothingSelected(AdapterView<?> adapterView) { 101 // Do nothing 102 } 103 }); 104 } 105 106 ((RadioGroup)findViewById(R.id.apiSelectionGroup)).check(R.id.aaudioButton); 107 findViewById(R.id.aaudioButton).setOnClickListener(new RadioButton.OnClickListener(){ 108 @Override 109 public void onClick(View v) { 110 if (((RadioButton)v).isChecked()) { 111 apiSelection = OBOE_API_AAUDIO; 112 } 113 } 114 }); 115 findViewById(R.id.slesButton).setOnClickListener(new RadioButton.OnClickListener(){ 116 @Override 117 public void onClick(View v) { 118 if (((RadioButton)v).isChecked()) { 119 apiSelection = OBOE_API_OPENSL_ES; 120 } 121 } 122 }); 123 124 LiveEffectEngine.setDefaultStreamValues(this); 125 } 126 EnableAudioApiUI(boolean enable)127 private void EnableAudioApiUI(boolean enable) { 128 if(apiSelection == OBOE_API_AAUDIO && !aaudioSupported) 129 { 130 apiSelection = OBOE_API_OPENSL_ES; 131 } 132 findViewById(R.id.slesButton).setEnabled(enable); 133 if(!aaudioSupported) { 134 findViewById(R.id.aaudioButton).setEnabled(false); 135 } else { 136 findViewById(R.id.aaudioButton).setEnabled(enable); 137 } 138 139 ((RadioGroup)findViewById(R.id.apiSelectionGroup)) 140 .check(apiSelection == OBOE_API_AAUDIO ? R.id.aaudioButton : R.id.slesButton); 141 } 142 143 @Override onStart()144 protected void onStart() { 145 super.onStart(); 146 setVolumeControlStream(AudioManager.STREAM_MUSIC); 147 } 148 149 @Override onResume()150 protected void onResume() { 151 super.onResume(); 152 LiveEffectEngine.create(); 153 aaudioSupported = LiveEffectEngine.isAAudioSupported(); 154 EnableAudioApiUI(true); 155 LiveEffectEngine.setAPI(apiSelection); 156 } 157 @Override onPause()158 protected void onPause() { 159 stopEffect(); 160 LiveEffectEngine.delete(); 161 super.onPause(); 162 } 163 toggleEffect()164 public void toggleEffect() { 165 if (isPlaying) { 166 stopEffect(); 167 } else { 168 LiveEffectEngine.setAPI(apiSelection); 169 startEffect(); 170 } 171 } 172 startEffect()173 private void startEffect() { 174 Log.d(TAG, "Attempting to start"); 175 176 if (!isRecordPermissionGranted()){ 177 requestRecordPermission(); 178 return; 179 } 180 181 boolean success = LiveEffectEngine.setEffectOn(true); 182 if (success) { 183 setSpinnersEnabled(false); 184 statusText.setText(R.string.status_playing); 185 toggleEffectButton.setText(R.string.stop_effect); 186 isPlaying = true; 187 EnableAudioApiUI(false); 188 } else { 189 statusText.setText(R.string.status_open_failed); 190 isPlaying = false; 191 } 192 } 193 stopEffect()194 private void stopEffect() { 195 Log.d(TAG, "Playing, attempting to stop"); 196 LiveEffectEngine.setEffectOn(false); 197 resetStatusView(); 198 toggleEffectButton.setText(R.string.start_effect); 199 isPlaying = false; 200 setSpinnersEnabled(true); 201 EnableAudioApiUI(true); 202 } 203 setSpinnersEnabled(boolean isEnabled)204 private void setSpinnersEnabled(boolean isEnabled){ 205 recordingDeviceSpinner.setEnabled(isEnabled); 206 playbackDeviceSpinner.setEnabled(isEnabled); 207 } 208 getRecordingDeviceId()209 private int getRecordingDeviceId(){ 210 return ((AudioDeviceListEntry)recordingDeviceSpinner.getSelectedItem()).getId(); 211 } 212 getPlaybackDeviceId()213 private int getPlaybackDeviceId(){ 214 return ((AudioDeviceListEntry)playbackDeviceSpinner.getSelectedItem()).getId(); 215 } 216 isRecordPermissionGranted()217 private boolean isRecordPermissionGranted() { 218 return (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == 219 PackageManager.PERMISSION_GRANTED); 220 } 221 requestRecordPermission()222 private void requestRecordPermission(){ 223 ActivityCompat.requestPermissions( 224 this, 225 new String[]{Manifest.permission.RECORD_AUDIO}, 226 AUDIO_EFFECT_REQUEST); 227 } 228 resetStatusView()229 private void resetStatusView() { 230 statusText.setText(R.string.status_warning); 231 } 232 233 @Override onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)234 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 235 @NonNull int[] grantResults) { 236 237 if (AUDIO_EFFECT_REQUEST != requestCode) { 238 super.onRequestPermissionsResult(requestCode, permissions, grantResults); 239 return; 240 } 241 242 if (grantResults.length != 1 || 243 grantResults[0] != PackageManager.PERMISSION_GRANTED) { 244 245 // User denied the permission, without this we cannot record audio 246 // Show a toast and update the status accordingly 247 statusText.setText(R.string.status_record_audio_denied); 248 Toast.makeText(getApplicationContext(), 249 getString(R.string.need_record_audio_permission), 250 Toast.LENGTH_SHORT) 251 .show(); 252 } else { 253 // Permission was granted, start live effect 254 toggleEffect(); 255 } 256 } 257 } 258