// Path: app/src/main/java/com/google/audio/asr/SpeechSessionListener.java
// public interface SpeechSessionListener {
//   /**
//    * Tells the client that the recognizer has had an error from which we cannot recover. It is safe
//    * to terminate the session.
//    */
//   void onSessionFatalError(int sessionID, Throwable error);
// 
//   /**
//    * Notifies that a new transcription result is available. If resultIsFinal is false, the results
//    * are subject to change.
//    */
//   void onResults(int sessionID, TranscriptionResult result, boolean resultIsFinal);
// 
//   /** Signals that no more audio should be sent to the recognizer. */
//   void onDoneListening(int sessionID);
// 
//   /**
//    * Notifies that it is safe to kill the session. Called when the recognizer is done returning
//    * results.
//    */
//   void onOkToTerminateSession(int sessionID);
// }
// 
// Path: app/src/main/java/com/google/audio/asr/TimeUtil.java
// public final class TimeUtil {
//   public static Instant toInstant(Timestamp t) {
//     return new Instant(Timestamps.toMillis(t));
//   }
// 
//   public static Timestamp toTimestamp(Instant t) {
//     return Timestamps.fromMillis(t.getMillis());
//   }
// 
//   public static Duration convert(com.google.protobuf.Duration d) {
//     return Duration.millis(Durations.toMillis(d));
//   }
// 
//   public static com.google.protobuf.Duration convert(Duration d) {
//     return Durations.fromMillis(d.getMillis());
//   }
//   private TimeUtil() {}
// }


import org.joda.time.Duration;
import org.joda.time.Instant;
import com.google.audio.asr.CloudSpeechStreamObserverParams;
import com.google.audio.asr.SpeechSessionListener;
import com.google.audio.asr.TimeUtil;
import com.google.audio.asr.TranscriptionResult;
import com.google.cloud.speech.v1p1beta1.SpeechRecognitionAlternative;
import com.google.cloud.speech.v1p1beta1.StreamingRecognitionResult;
import com.google.cloud.speech.v1p1beta1.StreamingRecognizeResponse;
import com.google.cloud.speech.v1p1beta1.StreamingRecognizeResponse.SpeechEventType;
import com.google.cloud.speech.v1p1beta1.WordInfo;
import com.google.common.base.Optional;
import com.google.common.flogger.FluentLogger;
import io.grpc.stub.StreamObserver;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/*
 * Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.audio.asr.cloud;


/**
 * Parses Cloud Speech API GRPC StreamingRecognizeResponse events into CloudSpeechSessionListener
 * events.
 * NOTE: that this object is stateful and needs to be re-instantiated for each streaming
 * request.
 *
 * Threading: All methods that implement the StreamObserver interface are to be called by gRPC. This
 * is the "results thread" as documented in the RepeatingRecognitionSession. The other public
 * functions are safe to call from another thread, they are called by CloudSpeechSession in the
 * current implementation.
 */
public class CloudSpeechStreamObserver implements StreamObserver<StreamingRecognizeResponse> {
  private static final FluentLogger logger = FluentLogger.forEnclosingClass();

  // The CloudSpeech API imposes a maximum streaming time of 5 minutes. In order to avoid hitting
  // this, but still be compatible with singleUtterance = false mode, which is required by some
  // models, we attempt to close the session after receiving a finalized result after being opened
  // for MIN_TIME_TO_KEEP_SESSION_OPEN.
  private static final Duration MIN_TIME_TO_KEEP_SESSION_OPEN =
      Duration.standardMinutes(4).plus(Duration.standardSeconds(30));

// Path: app/src/main/java/com/google/audio/asr/SpeechSessionListener.java
// public interface SpeechSessionListener {
//   /**
//    * Tells the client that the recognizer has had an error from which we cannot recover. It is safe
//    * to terminate the session.
//    */
//   void onSessionFatalError(int sessionID, Throwable error);
// 
//   /**
//    * Notifies that a new transcription result is available. If resultIsFinal is false, the results
//    * are subject to change.
//    */
//   void onResults(int sessionID, TranscriptionResult result, boolean resultIsFinal);
// 
//   /** Signals that no more audio should be sent to the recognizer. */
//   void onDoneListening(int sessionID);
// 
//   /**
//    * Notifies that it is safe to kill the session. Called when the recognizer is done returning
//    * results.
//    */
//   void onOkToTerminateSession(int sessionID);
// }
// 
// Path: app/src/main/java/com/google/audio/asr/TimeUtil.java
// public final class TimeUtil {
//   public static Instant toInstant(Timestamp t) {
//     return new Instant(Timestamps.toMillis(t));
//   }
// 
//   public static Timestamp toTimestamp(Instant t) {
//     return Timestamps.fromMillis(t.getMillis());
//   }
// 
//   public static Duration convert(com.google.protobuf.Duration d) {
//     return Duration.millis(Durations.toMillis(d));
//   }
// 
//   public static com.google.protobuf.Duration convert(Duration d) {
//     return Durations.fromMillis(d.getMillis());
//   }
//   private TimeUtil() {}
// }


// Path: app/src/main/java/com/google/audio/asr/cloud/CloudSpeechStreamObserver.java
import org.joda.time.Duration;
import org.joda.time.Instant;
import com.google.audio.asr.CloudSpeechStreamObserverParams;
import com.google.audio.asr.SpeechSessionListener;
import com.google.audio.asr.TimeUtil;
import com.google.audio.asr.TranscriptionResult;
import com.google.cloud.speech.v1p1beta1.SpeechRecognitionAlternative;
import com.google.cloud.speech.v1p1beta1.StreamingRecognitionResult;
import com.google.cloud.speech.v1p1beta1.StreamingRecognizeResponse;
import com.google.cloud.speech.v1p1beta1.StreamingRecognizeResponse.SpeechEventType;
import com.google.cloud.speech.v1p1beta1.WordInfo;
import com.google.common.base.Optional;
import com.google.common.flogger.FluentLogger;
import io.grpc.stub.StreamObserver;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/*
 * Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.audio.asr.cloud;


/**
 * Parses Cloud Speech API GRPC StreamingRecognizeResponse events into CloudSpeechSessionListener
 * events.
 * NOTE: that this object is stateful and needs to be re-instantiated for each streaming
 * request.
 *
 * Threading: All methods that implement the StreamObserver interface are to be called by gRPC. This
 * is the "results thread" as documented in the RepeatingRecognitionSession. The other public
 * functions are safe to call from another thread, they are called by CloudSpeechSession in the
 * current implementation.
 */
public class CloudSpeechStreamObserver implements StreamObserver<StreamingRecognizeResponse> {
  private static final FluentLogger logger = FluentLogger.forEnclosingClass();

  // The CloudSpeech API imposes a maximum streaming time of 5 minutes. In order to avoid hitting
  // this, but still be compatible with singleUtterance = false mode, which is required by some
  // models, we attempt to close the session after receiving a finalized result after being opened
  // for MIN_TIME_TO_KEEP_SESSION_OPEN.
  private static final Duration MIN_TIME_TO_KEEP_SESSION_OPEN =
      Duration.standardMinutes(4).plus(Duration.standardSeconds(30));

  private final SpeechSessionListener speechSessionListener;