Home | History | Annotate | Download | only in soundtrigger
      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
     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         // Make sure that the service is started, so even if our activity goes down, we'll still
    101         // have a request for it to run.
    102         startService(new Intent(getBaseContext(), SoundTriggerTestService.class));
    103 
    104         // Bind to SoundTriggerTestService.
    105         Intent intent = new Intent(this, SoundTriggerTestService.class);
    106         bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    107     }
    108 
    109     @Override
    110     protected void onDestroy() {
    111         super.onDestroy();
    112 
    113         // Unbind from the service.
    114         if (mService != null) {
    115             mService.setUserActivity(null);
    116             unbindService(mConnection);
    117         }
    118     }
    119 
    120     @Override
    121     public void addModel(UUID modelUuid, String name) {
    122         // Create a new widget for this model, and insert everything we'd need into the map.
    123         RadioButton button = new RadioButton(this);
    124         mModelRadioButtons.add(button);
    125         button.setText(name);
    126         button.setOnClickListener(new View.OnClickListener() {
    127             public void onClick(View v) {
    128                 onRadioButtonClicked(v);
    129             }
    130         });
    131         mButtonModelUuidMap.put(button, modelUuid);
    132         mModelButtons.put(modelUuid, button);
    133         mModelNames.put(modelUuid, name);
    134 
    135         // Sort all the radio buttons by name, then push them into the group in order.
    136         Collections.sort(mModelRadioButtons, new Comparator<RadioButton>(){
    137             @Override
    138             public int compare(RadioButton button0, RadioButton button1) {
    139                 return button0.getText().toString().compareTo(button1.getText().toString());
    140             }
    141         });
    142         mRadioGroup.removeAllViews();
    143         for (View v : mModelRadioButtons) {
    144             mRadioGroup.addView(v);
    145         }
    146 
    147         // If we don't have something selected, select this first thing.
    148         if (mSelectedModelUuid == null || mSelectedModelUuid.equals(modelUuid)) {
    149             button.setChecked(true);
    150             onRadioButtonClicked(button);
    151         }
    152     }
    153 
    154     @Override
    155     public void setModelState(UUID modelUuid, String state) {
    156         runOnUiThread(new Runnable() {
    157             @Override
    158             public void run() {
    159                 String newButtonText = mModelNames.get(modelUuid);
    160                 if (state != null) {
    161                     newButtonText += ": " + state;
    162                 }
    163                 mModelButtons.get(modelUuid).setText(newButtonText);
    164                 updateSelectModelSpecificUiElements();
    165             }
    166         });
    167     }
    168 
    169     @Override
    170     public void showMessage(String msg, boolean showToast) {
    171         // Append the message to the text field, then show the toast if requested.
    172         this.runOnUiThread(new Runnable() {
    173             @Override
    174             public void run() {
    175                 ((Editable) mDebugView.getText()).append(msg + "\n");
    176                 mScrollView.post(new Runnable() {
    177                     public void run() {
    178                         mScrollView.smoothScrollTo(0, mDebugView.getBottom());
    179                     }
    180                 });
    181                 if (showToast) {
    182                     Toast.makeText(SoundTriggerTestActivity.this, msg, Toast.LENGTH_SHORT).show();
    183                 }
    184             }
    185         });
    186     }
    187 
    188     @Override
    189     public void handleDetection(UUID modelUuid) {
    190         screenWakeup();
    191         mHandler.postDelayed(new Runnable() {
    192             @Override
    193             public void run() {
    194                 screenRelease();
    195             }
    196         }, 1000L);
    197     }
    198 
    199     private void screenWakeup() {
    200         if (mScreenWakelock == null) {
    201             PowerManager pm = ((PowerManager)getSystemService(POWER_SERVICE));
    202             mScreenWakelock =  pm.newWakeLock(
    203                     PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
    204         }
    205         mScreenWakelock.acquire();
    206     }
    207 
    208     private void screenRelease() {
    209         mScreenWakelock.release();
    210     }
    211 
    212     public void onLoadButtonClicked(View v) {
    213         if (mService == null) {
    214             Log.e(TAG, "Could not load sound model: not bound to SoundTriggerTestService");
    215         } else {
    216             mService.loadModel(mSelectedModelUuid);
    217         }
    218     }
    219 
    220     public void onUnloadButtonClicked(View v) {
    221         if (mService == null) {
    222            Log.e(TAG, "Can't unload model: not bound to SoundTriggerTestService");
    223         } else {
    224             mService.unloadModel(mSelectedModelUuid);
    225         }
    226     }
    227 
    228     public void onReloadButtonClicked(View v) {
    229         if (mService == null) {
    230             Log.e(TAG, "Can't reload model: not bound to SoundTriggerTestService");
    231         } else {
    232             mService.reloadModel(mSelectedModelUuid);
    233         }
    234     }
    235 
    236     public void onStartRecognitionButtonClicked(View v) {
    237         if (mService == null) {
    238             Log.e(TAG, "Can't start recognition: not bound to SoundTriggerTestService");
    239         } else {
    240             mService.startRecognition(mSelectedModelUuid);
    241         }
    242     }
    243 
    244     public void onStopRecognitionButtonClicked(View v) {
    245         if (mService == null) {
    246             Log.e(TAG, "Can't stop recognition: not bound to SoundTriggerTestService");
    247         } else {
    248             mService.stopRecognition(mSelectedModelUuid);
    249         }
    250     }
    251 
    252     public synchronized void onPlayTriggerButtonClicked(View v) {
    253         if (mService == null) {
    254             Log.e(TAG, "Can't play trigger audio: not bound to SoundTriggerTestService");
    255         } else {
    256             mService.playTriggerAudio(mSelectedModelUuid);
    257         }
    258     }
    259 
    260     public synchronized void onCaptureAudioCheckboxClicked(View v) {
    261         // See if we have the right permissions
    262         if (!mService.hasMicrophonePermission()) {
    263             requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO},
    264                     AUDIO_PERMISSIONS_REQUEST);
    265             return;
    266         } else {
    267             mService.setCaptureAudio(mSelectedModelUuid, mCaptureAudioCheckBox.isChecked());
    268         }
    269     }
    270 
    271     @Override
    272     public synchronized void onRequestPermissionsResult(int requestCode, String permissions[],
    273                                                         int[] grantResults) {
    274         if (requestCode == AUDIO_PERMISSIONS_REQUEST) {
    275             if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
    276                 // Make sure that the check box is set to false.
    277                 mCaptureAudioCheckBox.setChecked(false);
    278             }
    279             mService.setCaptureAudio(mSelectedModelUuid, mCaptureAudioCheckBox.isChecked());
    280         }
    281     }
    282 
    283     public synchronized void onPlayCapturedAudioButtonClicked(View v) {
    284         if (mService == null) {
    285             Log.e(TAG, "Can't play captured audio: not bound to SoundTriggerTestService");
    286         } else {
    287             mService.playCapturedAudio(mSelectedModelUuid);
    288         }
    289     }
    290 
    291     public synchronized void onRadioButtonClicked(View view) {
    292         // Is the button now checked?
    293         boolean checked = ((RadioButton) view).isChecked();
    294         if (checked) {
    295             mSelectedModelUuid = mButtonModelUuidMap.get(view);
    296             showMessage("Selected " + mModelNames.get(mSelectedModelUuid), false);
    297             updateSelectModelSpecificUiElements();
    298         }
    299     }
    300 
    301     private synchronized void updateSelectModelSpecificUiElements() {
    302         // Set the play trigger button to be enabled only if we actually have some audio.
    303         mPlayTriggerButton.setEnabled(mService.modelHasTriggerAudio((mSelectedModelUuid)));
    304         // Similar logic for the captured audio.
    305         mCaptureAudioCheckBox.setChecked(
    306                 mService.modelWillCaptureTriggerAudio(mSelectedModelUuid));
    307         mPlayCapturedAudioButton.setEnabled(mService.modelHasCapturedAudio((mSelectedModelUuid)));
    308     }
    309 
    310     private ServiceConnection mConnection = new ServiceConnection() {
    311         @Override
    312         public void onServiceConnected(ComponentName className, IBinder service) {
    313             synchronized (SoundTriggerTestActivity.this) {
    314                 // We've bound to LocalService, cast the IBinder and get LocalService instance
    315                 SoundTriggerTestBinder binder = (SoundTriggerTestBinder) service;
    316                 mService = binder.getService();
    317                 mService.setUserActivity(SoundTriggerTestActivity.this);
    318             }
    319         }
    320 
    321         @Override
    322         public void onServiceDisconnected(ComponentName arg0) {
    323             synchronized (SoundTriggerTestActivity.this) {
    324                 mService.setUserActivity(null);
    325                 mService = null;
    326             }
    327         }
    328     };
    329 }
    330