From 70fedf8d6caee1177ee891bbfff404dc48867c16 Mon Sep 17 00:00:00 2001 From: mdooley Date: Thu, 23 Nov 2017 08:31:05 -0800 Subject: Adding transcription rating feedback Allow users who have agreed to donate their voicemails to also provide transcription quality feedback. screenshot: https://drive.google.com/open?id=0B9o_KvtLkcuIajVtdFN3Y0Qydmx2NXJYN2N3OVA3N0h5UEdR Bug: 68712148 Test: manual and new unit tests PiperOrigin-RevId: 176774942 Change-Id: I08b9afbbefaedfb0de5199038a1d2769bd983855 --- .../com/android/voicemail/impl/AndroidManifest.xml | 5 ++ .../impl/VoicemailTranscriptionServiceGrpc.java | 70 +++++++++++++++- .../impl/transcribe/TranscriptionRatingHelper.java | 97 ++++++++++++++++++++++ .../transcribe/TranscriptionRatingService.java | 90 ++++++++++++++++++++ .../impl/transcribe/TranscriptionTask.java | 27 ++---- .../impl/transcribe/TranscriptionUtils.java | 25 +++++- .../impl/transcribe/grpc/TranscriptionClient.java | 11 +++ .../grpc/TranscriptionFeedbackResponseAsync.java | 35 ++++++++ .../transcribe/grpc/voicemail_transcription.proto | 2 +- 9 files changed, 338 insertions(+), 24 deletions(-) create mode 100644 java/com/android/voicemail/impl/transcribe/TranscriptionRatingHelper.java create mode 100644 java/com/android/voicemail/impl/transcribe/TranscriptionRatingService.java create mode 100644 java/com/android/voicemail/impl/transcribe/grpc/TranscriptionFeedbackResponseAsync.java (limited to 'java/com/android/voicemail') diff --git a/java/com/android/voicemail/impl/AndroidManifest.xml b/java/com/android/voicemail/impl/AndroidManifest.xml index 4cad2247c..53636092a 100644 --- a/java/com/android/voicemail/impl/AndroidManifest.xml +++ b/java/com/android/voicemail/impl/AndroidManifest.xml @@ -101,6 +101,11 @@ android:permission="android.permission.BIND_JOB_SERVICE" android:exported="false"/> + + METHOD_SEND_TRANSCRIPTION_FEEDBACK = + io.grpc.MethodDescriptor.create( + io.grpc.MethodDescriptor.MethodType.UNARY, + generateFullMethodName( + "google.internal.communications.voicemailtranscription.v1.VoicemailTranscriptionService", "SendTranscriptionFeedback"), + io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackRequest.getDefaultInstance()), + io.grpc.protobuf.lite.ProtoLiteUtils.marshaller(com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackResponse.getDefaultInstance())); /** * Creates a new async stub that supports all call types for the service @@ -136,6 +145,17 @@ public class VoicemailTranscriptionServiceGrpc { asyncUnimplementedUnaryCall(METHOD_GET_TRANSCRIPT, responseObserver); } + /** + *
+     * Uploads user's transcription feedback. Feedback will only be collected from
+     * user's who have consented to donate their voicemails.
+     * 
+ */ + public void sendTranscriptionFeedback(com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackRequest request, + io.grpc.stub.StreamObserver responseObserver) { + asyncUnimplementedUnaryCall(METHOD_SEND_TRANSCRIPTION_FEEDBACK, responseObserver); + } + @java.lang.Override public io.grpc.ServerServiceDefinition bindService() { return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) .addMethod( @@ -159,6 +179,13 @@ public class VoicemailTranscriptionServiceGrpc { com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest, com.google.internal.communications.voicemailtranscription.v1.GetTranscriptResponse>( this, METHODID_GET_TRANSCRIPT))) + .addMethod( + METHOD_SEND_TRANSCRIPTION_FEEDBACK, + asyncUnaryCall( + new MethodHandlers< + com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackRequest, + com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackResponse>( + this, METHODID_SEND_TRANSCRIPTION_FEEDBACK))) .build(); } } @@ -218,6 +245,18 @@ public class VoicemailTranscriptionServiceGrpc { asyncUnaryCall( getChannel().newCall(METHOD_GET_TRANSCRIPT, getCallOptions()), request, responseObserver); } + + /** + *
+     * Uploads user's transcription feedback. Feedback will only be collected from
+     * user's who have consented to donate their voicemails.
+     * 
+ */ + public void sendTranscriptionFeedback(com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackRequest request, + io.grpc.stub.StreamObserver responseObserver) { + asyncUnaryCall( + getChannel().newCall(METHOD_SEND_TRANSCRIPTION_FEEDBACK, getCallOptions()), request, responseObserver); + } } /** @@ -272,6 +311,17 @@ public class VoicemailTranscriptionServiceGrpc { return blockingUnaryCall( getChannel(), METHOD_GET_TRANSCRIPT, getCallOptions(), request); } + + /** + *
+     * Uploads user's transcription feedback. Feedback will only be collected from
+     * user's who have consented to donate their voicemails.
+     * 
+ */ + public com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackResponse sendTranscriptionFeedback(com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackRequest request) { + return blockingUnaryCall( + getChannel(), METHOD_SEND_TRANSCRIPTION_FEEDBACK, getCallOptions(), request); + } } /** @@ -329,11 +379,24 @@ public class VoicemailTranscriptionServiceGrpc { return futureUnaryCall( getChannel().newCall(METHOD_GET_TRANSCRIPT, getCallOptions()), request); } + + /** + *
+     * Uploads user's transcription feedback. Feedback will only be collected from
+     * user's who have consented to donate their voicemails.
+     * 
+ */ + public com.google.common.util.concurrent.ListenableFuture sendTranscriptionFeedback( + com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackRequest request) { + return futureUnaryCall( + getChannel().newCall(METHOD_SEND_TRANSCRIPTION_FEEDBACK, getCallOptions()), request); + } } private static final int METHODID_TRANSCRIBE_VOICEMAIL = 0; private static final int METHODID_TRANSCRIBE_VOICEMAIL_ASYNC = 1; private static final int METHODID_GET_TRANSCRIPT = 2; + private static final int METHODID_SEND_TRANSCRIPTION_FEEDBACK = 3; private static class MethodHandlers implements io.grpc.stub.ServerCalls.UnaryMethod, @@ -364,6 +427,10 @@ public class VoicemailTranscriptionServiceGrpc { serviceImpl.getTranscript((com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest) request, (io.grpc.stub.StreamObserver) responseObserver); break; + case METHODID_SEND_TRANSCRIPTION_FEEDBACK: + serviceImpl.sendTranscriptionFeedback((com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackRequest) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; default: throw new AssertionError(); } @@ -384,7 +451,8 @@ public class VoicemailTranscriptionServiceGrpc { return new io.grpc.ServiceDescriptor(SERVICE_NAME, METHOD_TRANSCRIBE_VOICEMAIL, METHOD_TRANSCRIBE_VOICEMAIL_ASYNC, - METHOD_GET_TRANSCRIPT); + METHOD_GET_TRANSCRIPT, + METHOD_SEND_TRANSCRIPTION_FEEDBACK); } } diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionRatingHelper.java b/java/com/android/voicemail/impl/transcribe/TranscriptionRatingHelper.java new file mode 100644 index 000000000..1cafacecf --- /dev/null +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionRatingHelper.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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 + * + * http://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.android.voicemail.impl.transcribe; + +import android.content.Context; +import android.net.Uri; +import com.android.dialer.common.concurrent.DialerExecutor; +import com.android.dialer.common.concurrent.DialerExecutorComponent; +import com.android.dialer.compat.android.provider.VoicemailCompat; +import com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackRequest; +import com.google.internal.communications.voicemailtranscription.v1.TranscriptionRating; +import com.google.internal.communications.voicemailtranscription.v1.TranscriptionRatingValue; +import com.google.protobuf.ByteString; + +/** + * Send voicemail transcription rating feedback to the server and record the fact that feedback was + * provided in the local database. + */ +public class TranscriptionRatingHelper { + + /** Callback invoked after the feedback has been recorded locally */ + public interface SuccessListener { + void onRatingSuccess(Uri voicemailUri); + } + + /** Callback invoked if there was an error recording the feedback */ + public interface FailureListener { + void onRatingFailure(Throwable t); + } + + /** + * Method for sending a user voicemail transcription feedback rating to the server and recording + * the fact that the voicemail was rated in the local database. + */ + public static void sendRating( + Context context, + TranscriptionRatingValue ratingValue, + Uri voicemailUri, + SuccessListener successListener, + FailureListener failureListener) { + DialerExecutorComponent.get(context) + .dialerExecutorFactory() + .createNonUiTaskBuilder(new RatingWorker(context, ratingValue, voicemailUri)) + .onSuccess(output -> successListener.onRatingSuccess(voicemailUri)) + .onFailure(e -> failureListener.onRatingFailure(e)) + .build() + .executeParallel(null); + } + + /** Worker class used to record a user's quality rating of a voicemail transcription. */ + private static class RatingWorker implements DialerExecutor.Worker { + private final Context context; + private final TranscriptionRatingValue ratingValue; + private final Uri voicemailUri; + + private RatingWorker(Context context, TranscriptionRatingValue ratingValue, Uri voicemailUri) { + this.context = context; + this.ratingValue = ratingValue; + this.voicemailUri = voicemailUri; + } + + @Override + public Void doInBackground(Void input) { + // Schedule a task to upload the feedback (requires network connectivity) + TranscriptionRatingService.scheduleTask(context, getFeedbackRequest()); + + // Record the fact that the transcription has been rated + TranscriptionDbHelper dbHelper = new TranscriptionDbHelper(context, voicemailUri); + dbHelper.setTranscriptionState(VoicemailCompat.TRANSCRIPTION_AVAILABLE_AND_RATED); + return null; + } + + private SendTranscriptionFeedbackRequest getFeedbackRequest() { + ByteString audioData = TranscriptionUtils.getAudioData(context, voicemailUri); + String voicemailId = TranscriptionUtils.getFingerprintFor(audioData); + TranscriptionRating rating = + TranscriptionRating.newBuilder() + .setTranscriptionId(voicemailId) + .setRatingValue(ratingValue) + .build(); + return SendTranscriptionFeedbackRequest.newBuilder().addRating(rating).build(); + } + } +} diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionRatingService.java b/java/com/android/voicemail/impl/transcribe/TranscriptionRatingService.java new file mode 100644 index 000000000..cff2c6d84 --- /dev/null +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionRatingService.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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 + * + * http://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.android.voicemail.impl.transcribe; + +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.app.job.JobWorkItem; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.support.annotation.WorkerThread; +import android.support.v4.app.JobIntentService; +import com.android.dialer.common.LogUtil; +import com.android.dialer.constants.ScheduledJobIds; +import com.android.voicemail.impl.transcribe.grpc.TranscriptionClientFactory; +import com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackRequest; +import com.google.protobuf.InvalidProtocolBufferException; + +/** + * JobScheduler service for uploading transcription feedback. This service requires a network + * connection. + */ +public class TranscriptionRatingService extends JobIntentService { + private static final String FEEDBACK_REQUEST_EXTRA = "feedback_request_extra"; + + /** Schedule a task to upload transcription rating feedback */ + public static boolean scheduleTask(Context context, SendTranscriptionFeedbackRequest request) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + LogUtil.enterBlock("TranscriptionRatingService.scheduleTask"); + ComponentName componentName = new ComponentName(context, TranscriptionRatingService.class); + JobInfo.Builder builder = + new JobInfo.Builder(ScheduledJobIds.VVM_TRANSCRIPTION_RATING_JOB, componentName) + .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); + JobScheduler scheduler = context.getSystemService(JobScheduler.class); + return scheduler.enqueue(builder.build(), makeWorkItem(request)) + == JobScheduler.RESULT_SUCCESS; + } else { + LogUtil.i("TranscriptionRatingService.scheduleTask", "not supported"); + return false; + } + } + + public TranscriptionRatingService() {} + + private static JobWorkItem makeWorkItem(SendTranscriptionFeedbackRequest request) { + Intent intent = new Intent(); + intent.putExtra(FEEDBACK_REQUEST_EXTRA, request.toByteArray()); + return new JobWorkItem(intent); + } + + @Override + @WorkerThread + protected void onHandleWork(Intent intent) { + LogUtil.enterBlock("TranscriptionRatingService.onHandleWork"); + + TranscriptionConfigProvider configProvider = new TranscriptionConfigProvider(this); + TranscriptionClientFactory factory = new TranscriptionClientFactory(this, configProvider); + try { + // Send rating to server + SendTranscriptionFeedbackRequest request = + SendTranscriptionFeedbackRequest.parseFrom( + intent.getByteArrayExtra(FEEDBACK_REQUEST_EXTRA)); + factory.getClient().sendTranscriptFeedbackRequest(request); + } catch (InvalidProtocolBufferException e) { + LogUtil.e("TranscriptionRatingService.onHandleWork", "failed to send feedback", e); + } finally { + factory.shutdown(); + } + } + + @Override + public void onDestroy() { + LogUtil.enterBlock("TranscriptionRatingService.onDestroy"); + super.onDestroy(); + } +} diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java index f3b1d587f..97cf89eef 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java @@ -15,7 +15,6 @@ */ package com.android.voicemail.impl.transcribe; -import android.annotation.TargetApi; import android.app.job.JobWorkItem; import android.content.Context; import android.net.Uri; @@ -37,8 +36,6 @@ import com.android.voicemail.impl.transcribe.grpc.TranscriptionResponse; import com.google.internal.communications.voicemailtranscription.v1.AudioFormat; import com.google.internal.communications.voicemailtranscription.v1.TranscriptionStatus; import com.google.protobuf.ByteString; -import java.io.IOException; -import java.io.InputStream; /** * Background task to get a voicemail transcription and update the database. @@ -71,8 +68,6 @@ public abstract class TranscriptionTask implements Runnable { protected AudioFormat encoding; protected volatile boolean cancelled; - static final String AMR_PREFIX = "#!AMR\n"; - /** Functional interface for sending requests to the transcription server */ public interface Request { TranscriptionResponse getResponse(TranscriptionClient client); @@ -226,8 +221,6 @@ public abstract class TranscriptionTask implements Runnable { databaseHelper.setTranscriptionState(newState); } - // Uses try-with-resource - @TargetApi(android.os.Build.VERSION_CODES.M) private boolean readAndValidateAudioFile() { if (voicemailUri == null) { VvmLog.i(TAG, "Transcriber.readAndValidateAudioFile, file not found."); @@ -236,15 +229,15 @@ public abstract class TranscriptionTask implements Runnable { VvmLog.i(TAG, "Transcriber.readAndValidateAudioFile, reading: " + voicemailUri); } - try (InputStream in = context.getContentResolver().openInputStream(voicemailUri)) { - audioData = ByteString.readFrom(in); - VvmLog.i(TAG, "Transcriber.readAndValidateAudioFile, read " + audioData.size() + " bytes"); - } catch (IOException e) { - VvmLog.e(TAG, "Transcriber.readAndValidateAudioFile", e); + audioData = TranscriptionUtils.getAudioData(context, voicemailUri); + if (audioData != null) { + VvmLog.i(TAG, "readAndValidateAudioFile, read " + audioData.size() + " bytes"); + } else { + VvmLog.i(TAG, "readAndValidateAudioFile, unable to read audio data for " + voicemailUri); return false; } - encoding = getAudioFormat(audioData); + encoding = TranscriptionUtils.getAudioFormat(audioData); if (encoding == AudioFormat.AUDIO_FORMAT_UNSPECIFIED) { VvmLog.i(TAG, "Transcriber.readAndValidateAudioFile, unknown encoding"); return false; @@ -253,15 +246,9 @@ public abstract class TranscriptionTask implements Runnable { return true; } - private static AudioFormat getAudioFormat(ByteString audioData) { - return audioData != null && audioData.startsWith(ByteString.copyFromUtf8(AMR_PREFIX)) - ? AudioFormat.AMR_NB_8KHZ - : AudioFormat.AUDIO_FORMAT_UNSPECIFIED; - } - @VisibleForTesting void setAudioDataForTesting(ByteString audioData) { this.audioData = audioData; - encoding = getAudioFormat(audioData); + encoding = TranscriptionUtils.getAudioFormat(audioData); } } diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionUtils.java b/java/com/android/voicemail/impl/transcribe/TranscriptionUtils.java index a001f179a..36b1400be 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionUtils.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionUtils.java @@ -16,17 +16,38 @@ package com.android.voicemail.impl.transcribe; import android.annotation.TargetApi; -import android.os.Build.VERSION_CODES; +import android.content.Context; +import android.net.Uri; import android.util.Base64; import com.android.dialer.common.Assert; +import com.google.internal.communications.voicemailtranscription.v1.AudioFormat; import com.google.protobuf.ByteString; +import java.io.IOException; +import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** Utility methods used by this transcription package. */ public class TranscriptionUtils { + static final String AMR_PREFIX = "#!AMR\n"; - @TargetApi(VERSION_CODES.O) + // Uses try-with-resource + @TargetApi(android.os.Build.VERSION_CODES.M) + static ByteString getAudioData(Context context, Uri voicemailUri) { + try (InputStream in = context.getContentResolver().openInputStream(voicemailUri)) { + return ByteString.readFrom(in); + } catch (IOException e) { + return null; + } + } + + static AudioFormat getAudioFormat(ByteString audioData) { + return audioData != null && audioData.startsWith(ByteString.copyFromUtf8(AMR_PREFIX)) + ? AudioFormat.AMR_NB_8KHZ + : AudioFormat.AUDIO_FORMAT_UNSPECIFIED; + } + + @TargetApi(android.os.Build.VERSION_CODES.O) static String getFingerprintFor(ByteString data) { Assert.checkArgument(data != null); try { diff --git a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClient.java b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClient.java index b18d95627..381cb3268 100644 --- a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClient.java +++ b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionClient.java @@ -17,6 +17,7 @@ package com.android.voicemail.impl.transcribe.grpc; import android.support.annotation.WorkerThread; import com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest; +import com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackRequest; import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest; import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailRequest; import com.google.internal.communications.voicemailtranscription.v1.VoicemailTranscriptionServiceGrpc; @@ -58,4 +59,14 @@ public class TranscriptionClient { return new GetTranscriptResponseAsync(e.getStatus()); } } + + @WorkerThread + public TranscriptionFeedbackResponseAsync sendTranscriptFeedbackRequest( + SendTranscriptionFeedbackRequest request) { + try { + return new TranscriptionFeedbackResponseAsync(stub.sendTranscriptionFeedback(request)); + } catch (StatusRuntimeException e) { + return new TranscriptionFeedbackResponseAsync(e.getStatus()); + } + } } diff --git a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionFeedbackResponseAsync.java b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionFeedbackResponseAsync.java new file mode 100644 index 000000000..bc6155bbd --- /dev/null +++ b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionFeedbackResponseAsync.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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 + * + * http://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.android.voicemail.impl.transcribe.grpc; + +import android.support.annotation.VisibleForTesting; +import com.android.dialer.common.Assert; +import com.google.internal.communications.voicemailtranscription.v1.SendTranscriptionFeedbackResponse; +import io.grpc.Status; + +/** Container for response and status objects for an asynchronous transcription feedback request */ +public class TranscriptionFeedbackResponseAsync extends TranscriptionResponse { + + @VisibleForTesting + public TranscriptionFeedbackResponseAsync(SendTranscriptionFeedbackResponse response) { + Assert.checkArgument(response != null); + } + + @VisibleForTesting + public TranscriptionFeedbackResponseAsync(Status status) { + super(status); + } +} diff --git a/java/com/android/voicemail/impl/transcribe/grpc/voicemail_transcription.proto b/java/com/android/voicemail/impl/transcribe/grpc/voicemail_transcription.proto index c46fb2176..8248b02a7 100644 --- a/java/com/android/voicemail/impl/transcribe/grpc/voicemail_transcription.proto +++ b/java/com/android/voicemail/impl/transcribe/grpc/voicemail_transcription.proto @@ -184,7 +184,7 @@ service VoicemailTranscriptionService { // Uploads user's transcription feedback. Feedback will only be collected from // user's who have consented to donate their voicemails. - rpc SendTranscriptionFeedback(SendTranscriptionFeedbackResponse) + rpc SendTranscriptionFeedback(SendTranscriptionFeedbackRequest) returns (SendTranscriptionFeedbackResponse) { } } -- cgit v1.2.3