Home | History | Annotate | Download | only in com.example.android.wearable.speaker
      1 /*
      2  * Copyright (C) 2015 Google Inc. All Rights Reserved.
      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.example.android.wearable.speaker;
     18 
     19 import android.Manifest;
     20 import android.content.Context;
     21 import android.content.pm.PackageManager;
     22 import android.media.AudioDeviceInfo;
     23 import android.media.AudioManager;
     24 import android.media.MediaPlayer;
     25 import android.os.Build;
     26 import android.os.Bundle;
     27 import android.os.CountDownTimer;
     28 import android.support.v4.app.ActivityCompat;
     29 import android.support.v4.content.ContextCompat;
     30 import android.support.wearable.activity.WearableActivity;
     31 import android.util.Log;
     32 import android.view.View;
     33 import android.widget.ImageView;
     34 import android.widget.ProgressBar;
     35 import android.widget.Toast;
     36 
     37 import java.util.concurrent.TimeUnit;
     38 
     39 /**
     40  * We first get the required permission to use the MIC. If it is granted, then we continue with
     41  * the application and present the UI with three icons: a MIC icon (if pressed, user can record up
     42  * to 10 seconds), a Play icon (if clicked, it wil playback the recorded audio file) and a music
     43  * note icon (if clicked, it plays an MP3 file that is included in the app).
     44  */
     45 public class MainActivity extends WearableActivity implements UIAnimation.UIStateListener,
     46         SoundRecorder.OnVoicePlaybackStateChangedListener {
     47 
     48     private static final String TAG = "MainActivity";
     49     private static final int PERMISSIONS_REQUEST_CODE = 100;
     50     private static final long COUNT_DOWN_MS = TimeUnit.SECONDS.toMillis(10);
     51     private static final long MILLIS_IN_SECOND = TimeUnit.SECONDS.toMillis(1);
     52     private static final String VOICE_FILE_NAME = "audiorecord.pcm";
     53     private MediaPlayer mMediaPlayer;
     54     private AppState mState = AppState.READY;
     55     private UIAnimation.UIState mUiState = UIAnimation.UIState.HOME;
     56     private SoundRecorder mSoundRecorder;
     57 
     58     private UIAnimation mUIAnimation;
     59     private ProgressBar mProgressBar;
     60     private CountDownTimer mCountDownTimer;
     61 
     62     enum AppState {
     63         READY, PLAYING_VOICE, PLAYING_MUSIC, RECORDING
     64     }
     65 
     66     @Override
     67     protected void onCreate(Bundle savedInstanceState) {
     68         super.onCreate(savedInstanceState);
     69         setContentView(R.layout.main_activity);
     70         mProgressBar = (ProgressBar) findViewById(R.id.progress);
     71         mProgressBar.setMax((int) (COUNT_DOWN_MS / MILLIS_IN_SECOND));
     72         setAmbientEnabled();
     73     }
     74 
     75     private void setProgressBar(long progressInMillis) {
     76         mProgressBar.setProgress((int) (progressInMillis / MILLIS_IN_SECOND));
     77     }
     78 
     79     @Override
     80     public void onUIStateChanged(UIAnimation.UIState state) {
     81         Log.d(TAG, "UI State is: " + state);
     82         if (mUiState == state) {
     83             return;
     84         }
     85         switch (state) {
     86             case MUSIC_UP:
     87                 mState = AppState.PLAYING_MUSIC;
     88                 mUiState = state;
     89                 playMusic();
     90                 break;
     91             case MIC_UP:
     92                 mState = AppState.RECORDING;
     93                 mUiState = state;
     94                 mSoundRecorder.startRecording();
     95                 setProgressBar(COUNT_DOWN_MS);
     96                 mCountDownTimer = new CountDownTimer(COUNT_DOWN_MS, MILLIS_IN_SECOND) {
     97                     @Override
     98                     public void onTick(long millisUntilFinished) {
     99                         mProgressBar.setVisibility(View.VISIBLE);
    100                         setProgressBar(millisUntilFinished);
    101                         Log.d(TAG, "Time Left: " + millisUntilFinished / MILLIS_IN_SECOND);
    102                     }
    103 
    104                     @Override
    105                     public void onFinish() {
    106                         mProgressBar.setProgress(0);
    107                         mProgressBar.setVisibility(View.INVISIBLE);
    108                         mSoundRecorder.stopRecording();
    109                         mUIAnimation.transitionToHome();
    110                         mUiState = UIAnimation.UIState.HOME;
    111                         mState = AppState.READY;
    112                         mCountDownTimer = null;
    113                     }
    114                 };
    115                 mCountDownTimer.start();
    116                 break;
    117             case SOUND_UP:
    118                 mState = AppState.PLAYING_VOICE;
    119                 mUiState = state;
    120                 mSoundRecorder.startPlay();
    121                 break;
    122             case HOME:
    123                 switch (mState) {
    124                     case PLAYING_MUSIC:
    125                         mState = AppState.READY;
    126                         mUiState = state;
    127                         stopMusic();
    128                         break;
    129                     case PLAYING_VOICE:
    130                         mState = AppState.READY;
    131                         mUiState = state;
    132                         mSoundRecorder.stopPlaying();
    133                         break;
    134                     case RECORDING:
    135                         mState = AppState.READY;
    136                         mUiState = state;
    137                         mSoundRecorder.stopRecording();
    138                         if (mCountDownTimer != null) {
    139                             mCountDownTimer.cancel();
    140                             mCountDownTimer = null;
    141                         }
    142                         mProgressBar.setVisibility(View.INVISIBLE);
    143                         setProgressBar(COUNT_DOWN_MS);
    144                         break;
    145                 }
    146                 break;
    147         }
    148     }
    149 
    150     /**
    151      * Plays back the MP3 file embedded in the application
    152      */
    153     private void playMusic() {
    154         if (mMediaPlayer == null) {
    155             mMediaPlayer = MediaPlayer.create(this, R.raw.sound);
    156             mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    157                 @Override
    158                 public void onCompletion(MediaPlayer mp) {
    159                     // we need to transition to the READY/Home state
    160                     Log.d(TAG, "Music Finished");
    161                     mUIAnimation.transitionToHome();
    162                 }
    163             });
    164         }
    165         mMediaPlayer.start();
    166     }
    167 
    168     /**
    169      * Stops the playback of the MP3 file.
    170      */
    171     private void stopMusic() {
    172         if (mMediaPlayer != null) {
    173             mMediaPlayer.stop();
    174             mMediaPlayer.release();
    175             mMediaPlayer = null;
    176         }
    177     }
    178 
    179     /**
    180      * Checks the permission that this app needs and if it has not been granted, it will
    181      * prompt the user to grant it, otherwise it shuts down the app.
    182      */
    183     private void checkPermissions() {
    184         boolean recordAudioPermissionGranted =
    185                 ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
    186                         == PackageManager.PERMISSION_GRANTED;
    187 
    188         if (recordAudioPermissionGranted) {
    189             start();
    190         } else {
    191             ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.RECORD_AUDIO},
    192                     PERMISSIONS_REQUEST_CODE);
    193         }
    194 
    195     }
    196 
    197     @Override
    198     public void onRequestPermissionsResult(int requestCode,
    199             String permissions[], int[] grantResults) {
    200         if (requestCode == PERMISSIONS_REQUEST_CODE) {
    201             if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    202                 start();
    203             } else {
    204                 // Permission has been denied before. At this point we should show a dialog to
    205                 // user and explain why this permission is needed and direct him to go to the
    206                 // Permissions settings for the app in the System settings. For this sample, we
    207                 // simply exit to get to the important part.
    208                 Toast.makeText(this, R.string.exiting_for_permissions, Toast.LENGTH_LONG).show();
    209                 finish();
    210             }
    211         }
    212     }
    213 
    214     /**
    215      * Starts the main flow of the application.
    216      */
    217     private void start() {
    218         mSoundRecorder = new SoundRecorder(this, VOICE_FILE_NAME, this);
    219         int[] thumbResources = new int[] {R.id.mic, R.id.play, R.id.music};
    220         ImageView[] thumbs = new ImageView[3];
    221         for(int i=0; i < 3; i++) {
    222             thumbs[i] = (ImageView) findViewById(thumbResources[i]);
    223         }
    224         View containerView = findViewById(R.id.container);
    225         ImageView expandedView = (ImageView) findViewById(R.id.expanded);
    226         int animationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
    227         mUIAnimation = new UIAnimation(containerView, thumbs, expandedView, animationDuration,
    228                 this);
    229     }
    230 
    231     @Override
    232     protected void onStart() {
    233         super.onStart();
    234         if (speakerIsSupported()) {
    235             checkPermissions();
    236         } else {
    237             findViewById(R.id.container2).setOnClickListener(new View.OnClickListener() {
    238                 @Override
    239                 public void onClick(View v) {
    240                     Toast.makeText(MainActivity.this, R.string.no_speaker_supported,
    241                             Toast.LENGTH_SHORT).show();
    242                 }
    243             });
    244         }
    245     }
    246 
    247     @Override
    248     protected void onStop() {
    249         if (mSoundRecorder != null) {
    250             mSoundRecorder.cleanup();
    251             mSoundRecorder = null;
    252         }
    253         if (mCountDownTimer != null) {
    254             mCountDownTimer.cancel();
    255         }
    256 
    257         if (mMediaPlayer != null) {
    258             mMediaPlayer.release();
    259             mMediaPlayer = null;
    260         }
    261         super.onStop();
    262     }
    263 
    264     @Override
    265     public void onPlaybackStopped() {
    266         mUIAnimation.transitionToHome();
    267         mUiState = UIAnimation.UIState.HOME;
    268         mState = AppState.READY;
    269     }
    270 
    271     /**
    272      * Determines if the wear device has a built-in speaker and if it is supported. Speaker, even if
    273      * physically present, is only supported in Android M+ on a wear device..
    274      */
    275     public final boolean speakerIsSupported() {
    276         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    277             PackageManager packageManager = getPackageManager();
    278             // The results from AudioManager.getDevices can't be trusted unless the device
    279             // advertises FEATURE_AUDIO_OUTPUT.
    280             if (!packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
    281                 return false;
    282             }
    283             AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    284             AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
    285             for (AudioDeviceInfo device : devices) {
    286                 if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) {
    287                     return true;
    288                 }
    289             }
    290         }
    291         return false;
    292     }
    293 }
    294