=======
Библиотека и пример приложения для абстрагирования доступа к сервису Amazon Alexa для приложений Android.
Прежде всего, моя цель в этом проекте — помочь другим, кто плохо разбирается в Java, Android или обоих, иметь возможность быстро и легко интегрировать платформу Amazon Alexa в свои собственные приложения.
Чтобы начать работу с платформой Amazon Alexa, загляните сюда: https://developer.amazon.com/appsandservices/solutions/alexa/alexa-voice-service/getting-started-with-the-alexa-voice-service.
Библиотека дополнена функциональностью версии API Alexa v20160207.
Интересно, что может сделать библиотека? Краткий пример трех основных функций (аудиособытия, записанные в реальном времени, преобразования текста в речь, предварительно записанные аудионамерения), а также пример кода можно найти в примере приложения.
Или посмотрите, на что способна библиотека, преобразованная в полный пакет с дополнительным постоянным прослушивателем: Alexa Listens
Доступ к большей части библиотеки можно получить через классы AlexaManager и AlexaAudioPlayer, оба из которых являются синглтонами.
buildscript {
repositories {
jcenter ()
}
...
}
allprojects {
repositories {
jcenter ()
}
}
compile 'com.willblaschko.android.alexa:AlexaAndroid:2.4.2'
private AlexaManager alexaManager ;
private AlexaAudioPlayer audioPlayer ;
private List < AvsItem > avsQueue = new ArrayList <>();
private void initAlexaAndroid (){
//get our AlexaManager instance for convenience
alexaManager = AlexaManager . getInstance ( this , PRODUCT_ID );
//instantiate our audio player
audioPlayer = AlexaAudioPlayer . getInstance ( this );
//Callback to be able to remove the current item and check queue once we've finished playing an item
audioPlayer . addCallback ( alexaAudioPlayerCallback );
}
//Our callback that deals with removing played items in our media player and then checking to see if more items exist
private AlexaAudioPlayer . Callback alexaAudioPlayerCallback = new AlexaAudioPlayer . Callback () {
@ Override
public void playerPrepared ( AvsItem pendingItem ) {
}
@ Override
public void itemComplete ( AvsItem completedItem ) {
avsQueue . remove ( completedItem );
checkQueue ();
}
@ Override
public boolean playerError ( int what , int extra ) {
return false ;
}
@ Override
public void dataError ( Exception e ) {
}
};
//async callback for commands sent to Alexa Voice
private AsyncCallback < AvsResponse , Exception > requestCallback = new AsyncCallback < AvsResponse , Exception >() {
@ Override
public void start () {
//your on start code
}
@ Override
public void success ( AvsResponse result ) {
Log . i ( TAG , "Voice Success" );
handleResponse ( result );
}
@ Override
public void failure ( Exception error ) {
//your on error code
}
@ Override
public void complete () {
//your on complete code
}
};
/**
* Handle the response sent back from Alexa's parsing of the Intent, these can be any of the AvsItem types (play, speak, stop, clear, listen)
* @param response a List<AvsItem> returned from the mAlexaManager.sendTextRequest() call in sendVoiceToAlexa()
*/
private void handleResponse ( AvsResponse response ){
if ( response != null ){
//if we have a clear queue item in the list, we need to clear the current queue before proceeding
//iterate backwards to avoid changing our array positions and getting all the nasty errors that come
//from doing that
for ( int i = response . size () - 1 ; i >= 0 ; i --){
if ( response . get ( i ) instanceof AvsReplaceAllItem || response . get ( i ) instanceof AvsReplaceEnqueuedItem ){
//clear our queue
avsQueue . clear ();
//remove item
response . remove ( i );
}
}
avsQueue . addAll ( response );
}
checkQueue ();
}
/**
* Check our current queue of items, and if we have more to parse (once we've reached a play or listen callback) then proceed to the
* next item in our list.
*
* We're handling the AvsReplaceAllItem in handleResponse() because it needs to clear everything currently in the queue, before
* the new items are added to the list, it should have no function here.
*/
private void checkQueue () {
//if we're out of things, hang up the phone and move on
if ( avsQueue . size () == 0 ) {
return ;
}
AvsItem current = avsQueue . get ( 0 );
if ( current instanceof AvsPlayRemoteItem ) {
//play a URL
if (! audioPlayer . isPlaying ()) {
audioPlayer . playItem (( AvsPlayRemoteItem ) current );
}
} else if ( current instanceof AvsPlayContentItem ) {
//play a URL
if (! audioPlayer . isPlaying ()) {
audioPlayer . playItem (( AvsPlayContentItem ) current );
}
} else if ( current instanceof AvsSpeakItem ) {
//play a sound file
if (! audioPlayer . isPlaying ()) {
audioPlayer . playItem (( AvsSpeakItem ) current );
}
} else if ( current instanceof AvsStopItem ) {
//stop our play
audioPlayer . stop ();
avsQueue . remove ( current );
} else if ( current instanceof AvsReplaceAllItem ) {
audioPlayer . stop ();
avsQueue . remove ( current );
} else if ( current instanceof AvsReplaceEnqueuedItem ) {
avsQueue . remove ( current );
} else if ( current instanceof AvsExpectSpeechItem ) {
//listen for user input
audioPlayer . stop ();
startListening ();
} else if ( current instanceof AvsSetVolumeItem ) {
setVolume ((( AvsSetVolumeItem ) current ). getVolume ());
avsQueue . remove ( current );
} else if ( current instanceof AvsAdjustVolumeItem ){
adjustVolume ((( AvsAdjustVolumeItem ) current ). getAdjustment ());
avsQueue . remove ( current );
} else if ( current instanceof AvsSetMuteItem ){
setMute ((( AvsSetMuteItem ) current ). isMute ());
avsQueue . remove ( current );
} else if ( current instanceof AvsMediaPlayCommandItem ){
//fake a hardware "play" press
sendMediaButton ( this , KeyEvent . KEYCODE_MEDIA_PLAY );
} else if ( current instanceof AvsMediaPauseCommandItem ){
//fake a hardware "pause" press
sendMediaButton ( this , KeyEvent . KEYCODE_MEDIA_PAUSE );
} else if ( current instanceof AvsMediaNextCommandItem ){
//fake a hardware "next" press
sendMediaButton ( this , KeyEvent . KEYCODE_MEDIA_NEXT );
} else if ( current instanceof AvsMediaPreviousCommandItem ){
//fake a hardware "previous" press
sendMediaButton ( this , KeyEvent . KEYCODE_MEDIA_PREVIOUS );
}
}
//our call to start listening when we get a AvsExpectSpeechItem
protected abstract void startListening ();
//adjust our device volume
private void adjustVolume ( long adjust ){
setVolume ( adjust , true );
}
//set our device volume
private void setVolume ( long volume ){
setVolume ( volume , false );
}
//set our device volume, handles both adjust and set volume to avoid repeating code
private void setVolume ( final long volume , final boolean adjust ){
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE );
final int max = am . getStreamMaxVolume ( AudioManager . STREAM_MUSIC );
long vol = am . getStreamVolume ( AudioManager . STREAM_MUSIC );
if ( adjust ){
vol += volume * max / 100 ;
} else {
vol = volume * max / 100 ;
}
am . setStreamVolume ( AudioManager . STREAM_MUSIC , ( int ) vol , AudioManager . FLAG_VIBRATE );
//confirm volume change
alexaManager . sendVolumeChangedEvent ( volume , vol == 0 , requestCallback );
}
//set device to mute
private void setMute ( final boolean isMute ){
AudioManager am = ( AudioManager ) getSystemService ( AUDIO_SERVICE );
am . setStreamMute ( AudioManager . STREAM_MUSIC , isMute );
//confirm device mute
alexaManager . sendMutedEvent ( isMute , requestCallback );
}
private static void sendMediaButton ( Context context , int keyCode ) {
KeyEvent keyEvent = new KeyEvent ( KeyEvent . ACTION_DOWN , keyCode );
Intent intent = new Intent ( Intent . ACTION_MEDIA_BUTTON );
intent . putExtra ( Intent . EXTRA_KEY_EVENT , keyEvent );
context . sendOrderedBroadcast ( intent , null );
keyEvent = new KeyEvent ( KeyEvent . ACTION_UP , keyCode );
intent = new Intent ( Intent . ACTION_MEDIA_BUTTON );
intent . putExtra ( Intent . EXTRA_KEY_EVENT , keyEvent );
context . sendOrderedBroadcast ( intent , null );
}
Вот краткий обзор кода, который, вероятно, потребуется для создания надежного приложения. Более подробную информацию можно найти в JavaDoc.
//Run an async check on whether we're logged in or not
alexaManager . checkLoggedIn ( mLoggedInCheck );
//Check if the user is already logged in to their Amazon account
alexaManager . checkLoggedIn ( AsyncCallback ...);
//Log the user in
alexaManager . logIn ( AuthorizationCallback ...);
private final static int MY_PERMISSIONS_REQUEST_RECORD_AUDIO = 1 ;
private static final int AUDIO_RATE = 16000 ;
private RawAudioRecorder recorder ;
private RecorderView recorderView ;
@ Override
public void onResume () {
super . onResume ();
//request API-23 permission for RECORD_AUDIO
if ( ContextCompat . checkSelfPermission ( getActivity (),
Manifest . permission . RECORD_AUDIO )
!= PackageManager . PERMISSION_GRANTED ) {
if (! ActivityCompat . shouldShowRequestPermissionRationale ( getActivity (),
Manifest . permission . RECORD_AUDIO )) {
ActivityCompat . requestPermissions ( getActivity (),
new String []{ Manifest . permission . RECORD_AUDIO },
MY_PERMISSIONS_REQUEST_RECORD_AUDIO );
}
}
}
//recieve RECORD_AUDIO permissions request
@ Override
public void onRequestPermissionsResult ( int requestCode ,
@ NonNull String permissions [],
@ NonNull int [] grantResults ) {
switch ( requestCode ) {
case MY_PERMISSIONS_REQUEST_RECORD_AUDIO : {
// If request is cancelled, the result arrays are empty.
if (!( grantResults . length > 0
&& grantResults [ 0 ] == PackageManager . PERMISSION_GRANTED )){
getActivity (). getSupportFragmentManager (). beginTransaction (). remove ( this ). commit ();
}
}
}
}
@ Override
public void onStop () {
super . onStop ();
//tear down our recorder on stop
if ( recorder != null ){
recorder . stop ();
recorder . release ();
recorder = null ;
}
}
@ Override
public void startListening () {
if ( recorder == null ){
recorder = new RawAudioRecorder ( AUDIO_RATE );
}
recorder . start ();
alexaManager . sendAudioRequest ( requestBody , getRequestCallback ());
}
//our streaming data requestBody
private DataRequestBody requestBody = new DataRequestBody () {
@ Override
public void writeTo ( BufferedSink sink ) throws IOException {
//while our recorder is not null and it is still recording, keep writing to POST data
while ( recorder != null && ! recorder . isPausing ()) {
if ( recorder != null ) {
final float rmsdb = recorder . getRmsdb ();
if ( recorderView != null ) {
recorderView . post ( new Runnable () {
@ Override
public void run () {
recorderView . setRmsdbLevel ( rmsdb );
}
});
}
if ( sink != null && recorder != null ) {
sink . write ( recorder . consumeRecording ());
}
}
//sleep and do it all over again
try {
Thread . sleep ( 25 );
} catch ( InterruptedException e ) {
e . printStackTrace ();
}
}
stopListening ();
}
};
//tear down our recorder
private void stopListening (){
if ( recorder != null ) {
recorder . stop ();
recorder . release ();
recorder = null ;
}
}
//send prerecorded audio to Alexa, parse the callback in requestCallback
try {
//open asset file
InputStream is = getActivity (). getAssets (). open ( "intros/joke.raw" );
byte [] fileBytes = new byte [ is . available ()];
is . read ( fileBytes );
is . close ();
mAlexaManager . sendAudioRequest ( fileBytes , getRequestCallback ());
} catch ( IOException e ) {
e . printStackTrace ();
}
//send a text request to Alexa, parse the callback in requestCallback
mAlexaManager . sendTextRequest ( text , requestCallback );
//Play an AvsPlayItem returned by our requests
audioPlayer . playItem ( AvsPlayAudioItem ...);
//Play an AvsSpeakItem returned by our requests
audioPlayer . playItem ( AvsSpeakItem ...);
Дайте мне знать, если вы хотите внести свой вклад в эту библиотеку!
Джош за работу с MP3