1 /* 2 * Copyright (C) 2014 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.android.test.soundtrigger; 18 19 import java.util.Collections; 20 import java.util.Comparator; 21 import java.util.HashMap; 22 import java.util.LinkedList; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.UUID; 26 27 import android.Manifest; 28 import android.app.Activity; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.ServiceConnection; 33 import android.content.pm.PackageManager; 34 import android.media.AudioManager; 35 import android.os.Bundle; 36 import android.os.Handler; 37 import android.os.IBinder; 38 import android.os.PowerManager; 39 import android.text.Editable; 40 import android.text.method.ScrollingMovementMethod; 41 import android.util.Log; 42 import android.view.View; 43 import android.view.WindowManager; 44 import android.widget.Button; 45 import android.widget.CheckBox; 46 import android.widget.RadioButton; 47 import android.widget.RadioGroup; 48 import android.widget.ScrollView; 49 import android.widget.TextView; 50 import android.widget.Toast; 51 52 import com.android.test.soundtrigger.SoundTriggerTestService.SoundTriggerTestBinder; 53 54 public class SoundTriggerTestActivity extends Activity implements SoundTriggerTestService.UserActivity { 55 private static final String TAG = "SoundTriggerTest"; 56 private static final int AUDIO_PERMISSIONS_REQUEST = 1; 57 58 private SoundTriggerTestService mService = null; 59 60 private static UUID mSelectedModelUuid = null; 61 62 private Map<RadioButton, UUID> mButtonModelUuidMap; 63 private Map<UUID, RadioButton> mModelButtons; 64 private Map<UUID, String> mModelNames; 65 private List<RadioButton> mModelRadioButtons; 66 67 private TextView mDebugView = null; 68 private ScrollView mScrollView = null; 69 private Button mPlayTriggerButton = null; 70 private PowerManager.WakeLock mScreenWakelock; 71 private Handler mHandler; 72 private RadioGroup mRadioGroup; 73 private CheckBox mCaptureAudioCheckBox; 74 private Button mPlayCapturedAudioButton = null; 75 76 @Override onCreate(Bundle savedInstanceState)77 protected void onCreate(Bundle savedInstanceState) { 78 // Make sure that this activity can punch through the lockscreen if needed. 79 getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | 80 WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); 81 82 super.onCreate(savedInstanceState); 83 setContentView(R.layout.main); 84 mDebugView = findViewById(R.id.console); 85 mScrollView = findViewById(R.id.scroller_id); 86 mRadioGroup = findViewById(R.id.model_group_id); 87 mPlayTriggerButton = findViewById(R.id.play_trigger_id); 88 mDebugView.setText(mDebugView.getText(), TextView.BufferType.EDITABLE); 89 mDebugView.setMovementMethod(new ScrollingMovementMethod()); 90 mCaptureAudioCheckBox = findViewById(R.id.caputre_check_box); 91 mPlayCapturedAudioButton = findViewById(R.id.play_captured_id); 92 mHandler = new Handler(); 93 mButtonModelUuidMap = new HashMap(); 94 mModelButtons = new HashMap(); 95 mModelNames = new HashMap(); 96 mModelRadioButtons = new LinkedList(); 97 98 setVolumeControlStream(AudioManager.STREAM_MUSIC); 99 100 requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 101 AUDIO_PERMISSIONS_REQUEST); 102 } 103 104 @Override onDestroy()105 protected void onDestroy() { 106 super.onDestroy(); 107 108 // Unbind from the service. 109 if (mService != null) { 110 mService.setUserActivity(null); 111 unbindService(mConnection); 112 } 113 } 114 115 @Override addModel(UUID modelUuid, String name)116 public void addModel(UUID modelUuid, String name) { 117 // Create a new widget for this model, and insert everything we'd need into the map. 118 RadioButton button = new RadioButton(this); 119 mModelRadioButtons.add(button); 120 button.setText(name); 121 button.setOnClickListener(new View.OnClickListener() { 122 public void onClick(View v) { 123 onRadioButtonClicked(v); 124 } 125 }); 126 mButtonModelUuidMap.put(button, modelUuid); 127 mModelButtons.put(modelUuid, button); 128 mModelNames.put(modelUuid, name); 129 130 // Sort all the radio buttons by name, then push them into the group in order. 131 Collections.sort(mModelRadioButtons, new Comparator<RadioButton>(){ 132 @Override 133 public int compare(RadioButton button0, RadioButton button1) { 134 return button0.getText().toString().compareTo(button1.getText().toString()); 135 } 136 }); 137 mRadioGroup.removeAllViews(); 138 for (View v : mModelRadioButtons) { 139 mRadioGroup.addView(v); 140 } 141 142 // If we don't have something selected, select this first thing. 143 if (mSelectedModelUuid == null || mSelectedModelUuid.equals(modelUuid)) { 144 button.setChecked(true); 145 onRadioButtonClicked(button); 146 } 147 } 148 149 @Override setModelState(UUID modelUuid, String state)150 public void setModelState(UUID modelUuid, String state) { 151 runOnUiThread(new Runnable() { 152 @Override 153 public void run() { 154 String newButtonText = mModelNames.get(modelUuid); 155 if (state != null) { 156 newButtonText += ": " + state; 157 } 158 mModelButtons.get(modelUuid).setText(newButtonText); 159 updateSelectModelSpecificUiElements(); 160 } 161 }); 162 } 163 164 @Override showMessage(String msg, boolean showToast)165 public void showMessage(String msg, boolean showToast) { 166 // Append the message to the text field, then show the toast if requested. 167 this.runOnUiThread(new Runnable() { 168 @Override 169 public void run() { 170 ((Editable) mDebugView.getText()).append(msg + "\n"); 171 mScrollView.post(new Runnable() { 172 public void run() { 173 mScrollView.smoothScrollTo(0, mDebugView.getBottom()); 174 } 175 }); 176 if (showToast) { 177 Toast.makeText(SoundTriggerTestActivity.this, msg, Toast.LENGTH_SHORT).show(); 178 } 179 } 180 }); 181 } 182 183 @Override handleDetection(UUID modelUuid)184 public void handleDetection(UUID modelUuid) { 185 screenWakeup(); 186 mHandler.postDelayed(new Runnable() { 187 @Override 188 public void run() { 189 screenRelease(); 190 } 191 }, 1000L); 192 } 193 screenWakeup()194 private void screenWakeup() { 195 if (mScreenWakelock == null) { 196 PowerManager pm = ((PowerManager)getSystemService(POWER_SERVICE)); 197 mScreenWakelock = pm.newWakeLock( 198 PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG); 199 } 200 mScreenWakelock.acquire(); 201 } 202 screenRelease()203 private void screenRelease() { 204 mScreenWakelock.release(); 205 } 206 onLoadButtonClicked(View v)207 public void onLoadButtonClicked(View v) { 208 if (mService == null) { 209 Log.e(TAG, "Could not load sound model: not bound to SoundTriggerTestService"); 210 } else { 211 mService.loadModel(mSelectedModelUuid); 212 } 213 } 214 onUnloadButtonClicked(View v)215 public void onUnloadButtonClicked(View v) { 216 if (mService == null) { 217 Log.e(TAG, "Can't unload model: not bound to SoundTriggerTestService"); 218 } else { 219 mService.unloadModel(mSelectedModelUuid); 220 } 221 } 222 onReloadButtonClicked(View v)223 public void onReloadButtonClicked(View v) { 224 if (mService == null) { 225 Log.e(TAG, "Can't reload model: not bound to SoundTriggerTestService"); 226 } else { 227 mService.reloadModel(mSelectedModelUuid); 228 } 229 } 230 onStartRecognitionButtonClicked(View v)231 public void onStartRecognitionButtonClicked(View v) { 232 if (mService == null) { 233 Log.e(TAG, "Can't start recognition: not bound to SoundTriggerTestService"); 234 } else { 235 mService.startRecognition(mSelectedModelUuid); 236 } 237 } 238 onStopRecognitionButtonClicked(View v)239 public void onStopRecognitionButtonClicked(View v) { 240 if (mService == null) { 241 Log.e(TAG, "Can't stop recognition: not bound to SoundTriggerTestService"); 242 } else { 243 mService.stopRecognition(mSelectedModelUuid); 244 } 245 } 246 onPlayTriggerButtonClicked(View v)247 public synchronized void onPlayTriggerButtonClicked(View v) { 248 if (mService == null) { 249 Log.e(TAG, "Can't play trigger audio: not bound to SoundTriggerTestService"); 250 } else { 251 mService.playTriggerAudio(mSelectedModelUuid); 252 } 253 } 254 onGetModelStateButtonClicked(View v)255 public synchronized void onGetModelStateButtonClicked(View v) { 256 if (mService == null) { 257 Log.e(TAG, "Can't get model state: not bound to SoundTriggerTestService"); 258 } else { 259 mService.getModelState(mSelectedModelUuid); 260 } 261 } 262 onCaptureAudioCheckboxClicked(View v)263 public synchronized void onCaptureAudioCheckboxClicked(View v) { 264 // See if we have the right permissions 265 if (mService == null) { 266 Log.e(TAG, "Can't set capture audio: not bound to SoundTriggerTestService"); 267 } else { 268 if (!mService.hasMicrophonePermission()) { 269 requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, 270 AUDIO_PERMISSIONS_REQUEST); 271 return; 272 } else { 273 mService.setCaptureAudio(mSelectedModelUuid, mCaptureAudioCheckBox.isChecked()); 274 } 275 } 276 } 277 278 @Override onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)279 public synchronized void onRequestPermissionsResult(int requestCode, String permissions[], 280 int[] grantResults) { 281 if (requestCode == AUDIO_PERMISSIONS_REQUEST) { 282 if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { 283 // Make sure that the check box is set to false. 284 mCaptureAudioCheckBox.setChecked(false); 285 } else { 286 // After granted Record_Audio permission, start and bind the service. 287 // so we can run that sound trigger capability, 288 // even if our activity goes down, we'll still have a request for it to run. 289 startService(new Intent(getBaseContext(), SoundTriggerTestService.class)); 290 // Bind to SoundTriggerTestService. 291 Intent intent = new Intent(this, SoundTriggerTestService.class); 292 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 293 } 294 } 295 } 296 onPlayCapturedAudioButtonClicked(View v)297 public synchronized void onPlayCapturedAudioButtonClicked(View v) { 298 if (mService == null) { 299 Log.e(TAG, "Can't play captured audio: not bound to SoundTriggerTestService"); 300 } else { 301 mService.playCapturedAudio(mSelectedModelUuid); 302 } 303 } 304 onRadioButtonClicked(View view)305 public synchronized void onRadioButtonClicked(View view) { 306 // Is the button now checked? 307 boolean checked = ((RadioButton) view).isChecked(); 308 if (checked) { 309 mSelectedModelUuid = mButtonModelUuidMap.get(view); 310 showMessage("Selected " + mModelNames.get(mSelectedModelUuid), false); 311 updateSelectModelSpecificUiElements(); 312 } 313 } 314 updateSelectModelSpecificUiElements()315 private synchronized void updateSelectModelSpecificUiElements() { 316 // Set the play trigger button to be enabled only if we actually have some audio. 317 mPlayTriggerButton.setEnabled(mService.modelHasTriggerAudio((mSelectedModelUuid))); 318 // Similar logic for the captured audio. 319 mCaptureAudioCheckBox.setChecked( 320 mService.modelWillCaptureTriggerAudio(mSelectedModelUuid)); 321 mPlayCapturedAudioButton.setEnabled(mService.modelHasCapturedAudio((mSelectedModelUuid))); 322 } 323 324 private ServiceConnection mConnection = new ServiceConnection() { 325 @Override 326 public void onServiceConnected(ComponentName className, IBinder service) { 327 synchronized (SoundTriggerTestActivity.this) { 328 // We've bound to LocalService, cast the IBinder and get LocalService instance 329 SoundTriggerTestBinder binder = (SoundTriggerTestBinder) service; 330 mService = binder.getService(); 331 mService.setUserActivity(SoundTriggerTestActivity.this); 332 } 333 } 334 335 @Override 336 public void onServiceDisconnected(ComponentName arg0) { 337 synchronized (SoundTriggerTestActivity.this) { 338 mService.setUserActivity(null); 339 mService = null; 340 } 341 } 342 }; 343 } 344