/**
  * Analyzes response from Amazon Lex service. Returns a {@link Runnable} with the next step, which
  * is usually a callback method in the {@link InteractionListener} object.
  *
  * @param handler {@link Handler}, to interact with app components in the main thread.
  * @param result {@link PostContentResult}, response from the Amazon Lex service.
  * @param client {@link InteractionClient}, reference to this object.
  * @param responseMode {@link ResponseType}, current response type.
  */
 private void processResponse(
     final Handler handler,
     final PostContentResult result,
     final InteractionClient client,
     final ResponseType responseMode,
     final ResponseType requestMode) {
   Runnable response;
   try {
     setBusyState(NOT_BUSY);
     final Response serviceResponse = new Response(result);
     if (DialogState.Failed.toString().equals(result.getDialogState())) {
       // Amazon Lex service reported an error.
       response =
           new Runnable() {
             @Override
             public void run() {
               interactionListener.onInteractionError(
                   serviceResponse,
                   new DialogFailedException("Failed to fulfill current request."));
             }
           };
     } else if (DialogState.ReadyForFulfillment.equals(result.getDialogState())) {
       // The current dlalog is ready for fulfilled by the client.
       response =
           new Runnable() {
             @Override
             public void run() {
               interactionListener.onReadyForFulfillment(new Response(result));
             }
           };
     } else if (DialogState.Fulfilled.toString().equals(result.getDialogState())) {
       // Request was successfully fulfilled, no further action required.
       response =
           new Runnable() {
             @Override
             public void run() {
               interactionListener.promptUserToRespond(serviceResponse, null);
             }
           };
     } else {
       // User's response is required to continue.
       final LexServiceContinuation continuation =
           new LexServiceContinuation(client, responseMode, requestMode);
       // set the session attributes on the continuation
       continuation.setSessionAttributes(serviceResponse.getSessionAttributes());
       response =
           new Runnable() {
             @Override
             public void run() {
               interactionListener.promptUserToRespond(serviceResponse, continuation);
             }
           };
     }
   } catch (final Exception e) {
     response =
         new Runnable() {
           @Override
           public void run() {
             interactionListener.onInteractionError(null, e);
           }
         };
   } finally {
     setBusyState(NOT_BUSY);
   }
   handler.post(response);
 }
  /**
   * Invokes the Android {@link MediaPlayer} to playback audio if audio playback was requested, and
   * continues to analyze the response. If the response does not contain audio stream or if audio
   * playback was not requested, continues to analyze the response.
   *
   * @param handler {@link Handler}, to interact with app components in the main thread.
   * @param result {@link PostContentResult}, response from the Amazon Lex service.
   * @param client {@link InteractionClient}, reference to this object.
   * @param responseMode {@link ResponseType}, current response type.
   */
  private void processResponseAudioPlayback(
      final Handler handler,
      final PostContentResult result,
      final InteractionClient client,
      final ResponseType responseMode,
      final ResponseType requestType) {
    // Check if response is audio and audio playback is requested.
    if (ResponseType.AUDIO_MPEG.equals(responseMode) && interactionConfig.isEnableAudioPlayback()) {
      this.lMediaPlayer = new MediaPlayer();
      this.lMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
      try {
        setAudioPlaybackState(BUSY);
        File tempAudioFile = File.createTempFile("lex_temp_response", "mp3", context.getFilesDir());
        tempAudioFile.deleteOnExit();
        // Media player listeners.
        lMediaPlayer.setOnErrorListener(
            new MediaPlayer.OnErrorListener() {
              @Override
              public boolean onError(final MediaPlayer mp, final int what, final int extra) {
                if (interactionListener != null) {
                  final Runnable appCallback =
                      new Runnable() {
                        @Override
                        public void run() {
                          audioPlaybackListener.onAudioPlaybackError(
                              new AudioPlaybackException(
                                  String.format(
                                      Locale.US,
                                      "MediaPlayer error: \"what\": %d, \"extra\":%d",
                                      what,
                                      extra)));
                        }
                      };
                  handler.post(appCallback);
                }
                return false;
              }
            });

        lMediaPlayer.setOnPreparedListener(
            new MediaPlayer.OnPreparedListener() {
              @Override
              public void onPrepared(MediaPlayer mp) {
                if (audioPlaybackListener != null) {
                  final Runnable appCallback =
                      new Runnable() {
                        @Override
                        public void run() {
                          audioPlaybackListener.onAudioPlaybackStarted();
                        }
                      };
                  handler.post(appCallback);
                }
                mp.start();
              }
            });

        lMediaPlayer.setOnCompletionListener(
            new MediaPlayer.OnCompletionListener() {
              @Override
              public void onCompletion(MediaPlayer mp) {
                setAudioPlaybackState(NOT_BUSY);
                if (audioPlaybackListener != null) {
                  final Runnable appCallback =
                      new Runnable() {
                        @Override
                        public void run() {
                          audioPlaybackListener.onAudioPlayBackCompleted();
                        }
                      };
                  handler.post(appCallback);
                }
                try {
                  if (lMediaPlayer.isPlaying() || lMediaPlayer.isLooping()) {
                    lMediaPlayer.stop();
                  }
                  lMediaPlayer.release();
                } catch (final Exception e) {
                  Log.e(TAG, "InteractionClient: Error while releasing MediaPlayer", e);
                } finally {
                  lMediaPlayer = null;
                }
              }
            });

        final InputStream audioStream = result.getAudioStream();
        tempAudioFile = File.createTempFile("lex_temp_response", "dat", context.getFilesDir());
        tempAudioFile.deleteOnExit();
        final FileOutputStream audioOut = new FileOutputStream(tempAudioFile);
        final byte buffer[] = new byte[16384];
        int length;
        while ((length = audioStream.read(buffer)) != -1) {
          audioOut.write(buffer, 0, length);
        }
        audioOut.close();
        final FileInputStream audioIn = new FileInputStream(tempAudioFile);
        lMediaPlayer.setDataSource(audioIn.getFD());
        lMediaPlayer.prepare();
        processResponse(handler, result, client, responseMode, requestType);
      } catch (final Exception e) {
        // Playback failed.
        if (audioPlaybackListener != null) {
          final Runnable appCallback =
              new Runnable() {
                @Override
                public void run() {
                  audioPlaybackListener.onAudioPlaybackError(
                      new LexClientException("Audio playback failed", e));
                }
              };
          handler.post(appCallback);
        }
        try {
          if (lMediaPlayer.isPlaying() || lMediaPlayer.isLooping()) {
            lMediaPlayer.stop();
          }
          lMediaPlayer.release();
          lMediaPlayer = null;
        } catch (final Exception exp) {
          Log.e(TAG, "InteractionClient: Error while releasing MediaPlayer", exp);
        }
        processResponse(handler, result, client, responseMode, requestType);
      } finally {
        setAudioPlaybackState(NOT_BUSY);
      }

    } else {
      processResponse(handler, result, client, responseMode, requestType);
    }
  }