From 295d9ad53628eba12d635b35d6b51eb3cae8a4aa Mon Sep 17 00:00:00 2001 From: linyuh Date: Tue, 28 Nov 2017 14:32:46 -0800 Subject: Disable phone number formatting on the dialpad when making domestic call to Argentina mobile numbers. Bug: 30224215 Test: DialerPhoneNumberFormattingTextWatcherTest, DialpadFragmentIntegrationTest PiperOrigin-RevId: 177218942 Change-Id: Id6eaaad2f6d81e591d59a1d8444f45fb06f3f8d5 --- .../dialer/dialpadview/DialpadFragment.java | 211 +++++++++++++++++++-- 1 file changed, 198 insertions(+), 13 deletions(-) (limited to 'java') diff --git a/java/com/android/dialer/dialpadview/DialpadFragment.java b/java/com/android/dialer/dialpadview/DialpadFragment.java index 4126443bc..69186d87c 100644 --- a/java/com/android/dialer/dialpadview/DialpadFragment.java +++ b/java/com/android/dialer/dialpadview/DialpadFragment.java @@ -89,8 +89,11 @@ import com.android.dialer.telecom.TelecomUtil; import com.android.dialer.util.CallUtil; import com.android.dialer.util.PermissionsUtil; import com.android.dialer.widget.FloatingActionButtonController; +import com.google.common.base.Optional; import java.util.HashSet; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** Fragment that displays a twelve-key phone dialpad. */ public class DialpadFragment extends Fragment @@ -129,6 +132,9 @@ public class DialpadFragment extends Fragment private static final String EXTRA_SEND_EMPTY_FLASH = "com.android.phone.extra.SEND_EMPTY_FLASH"; private static final String PREF_DIGITS_FILLED_BY_INTENT = "pref_digits_filled_by_intent"; + + private static Optional currentCountryIsoForTesting = Optional.absent(); + private final Object mToneGeneratorLock = new Object(); /** Set of dialpad keys that are currently being pressed */ private final HashSet mPressedDialpadKeys = new HashSet<>(12); @@ -156,7 +162,6 @@ public class DialpadFragment extends Fragment // determines if we want to playback local DTMF tones. private boolean mDTMFToneEnabled; - private String mCurrentCountryIso; private CallStateReceiver mCallStateReceiver; private boolean mWasEmptyBeforeTextChange; /** @@ -324,8 +329,6 @@ public class DialpadFragment extends Fragment mFirstLaunch = state == null; - mCurrentCountryIso = GeoUtil.getCurrentCountryIso(getActivity()); - mProhibitedPhoneNumberRegexp = getResources().getString(R.string.config_prohibited_phone_number_regexp); @@ -377,8 +380,7 @@ public class DialpadFragment extends Fragment mDigits.addTextChangedListener(this); mDigits.setElegantTextHeight(false); - initPhoneNumberFormattingTextWatcherExecutor.executeSerial( - GeoUtil.getCurrentCountryIso(getActivity())); + initPhoneNumberFormattingTextWatcherExecutor.executeSerial(getCurrentCountryIso()); // Check for the presence of the keypad View oneButton = fragmentView.findViewById(R.id.one); @@ -422,6 +424,19 @@ public class DialpadFragment extends Fragment return fragmentView; } + private String getCurrentCountryIso() { + if (currentCountryIsoForTesting.isPresent()) { + return currentCountryIsoForTesting.get(); + } + + return GeoUtil.getCurrentCountryIso(getActivity()); + } + + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + public static void setCurrentCountryIsoForTesting(String countryCode) { + currentCountryIsoForTesting = Optional.of(countryCode); + } + private boolean isLayoutReady() { return mDigits != null; } @@ -561,7 +576,7 @@ public class DialpadFragment extends Fragment /** Sets formatted digits to digits field. */ private void setFormattedDigits(String data, String normalizedNumber) { - final String formatted = getFormattedDigits(data, normalizedNumber, mCurrentCountryIso); + final String formatted = getFormattedDigits(data, normalizedNumber, getCurrentCountryIso()); if (!TextUtils.isEmpty(formatted)) { Editable digits = mDigits.getText(); digits.replace(0, digits.length(), formatted); @@ -1717,19 +1732,189 @@ public class DialpadFragment extends Fragment } /** - * Input: the ISO 3166-1 two letters country code of the country the user is in + * A worker that helps formatting the phone number as the user types it in. * - *

Output: PhoneNumberFormattingTextWatcher. Note: It is unusual to return a non-data value - * from a worker, but it is a limitation in libphonenumber API that the watcher cannot be - * initialized on the main thread. + *

Input: the ISO 3166-1 two-letter country code of the country the user is in. + * + *

Output: an instance of {@link DialerPhoneNumberFormattingTextWatcher}. Note: It is unusual + * to return a non-data value from a worker. But {@link DialerPhoneNumberFormattingTextWatcher} + * depends on libphonenumber API, which cannot be initialized on the main thread. */ private static class InitPhoneNumberFormattingTextWatcherWorker - implements Worker { + implements Worker { @Nullable @Override - public PhoneNumberFormattingTextWatcher doInBackground(@Nullable String countryCode) { - return new PhoneNumberFormattingTextWatcher(countryCode); + public DialerPhoneNumberFormattingTextWatcher doInBackground(@Nullable String countryCode) { + return new DialerPhoneNumberFormattingTextWatcher(countryCode); + } + } + + /** + * An extension of Android telephony's {@link PhoneNumberFormattingTextWatcher}. This watcher + * skips formatting Argentina mobile numbers for domestic calls. + * + *

As of Nov. 28, 2017, the as-you-type-formatting provided by libphonenumber's + * AsYouTypeFormatter (which {@link PhoneNumberFormattingTextWatcher} depends on) can't correctly + * format Argentina mobile numbers for domestic calls (a bug). We temporarily disable the + * formatting for such numbers until libphonenumber is fixed (which will come as early as the next + * Android release). + */ + @VisibleForTesting + public static class DialerPhoneNumberFormattingTextWatcher + extends PhoneNumberFormattingTextWatcher { + private static final Pattern AR_DOMESTIC_CALL_MOBILE_NUMBER_PATTERN; + + // This static initialization block builds a pattern for domestic calls to Argentina mobile + // numbers: + // (1) Local calls: 15 + // (2) Long distance calls: 15 + // See https://en.wikipedia.org/wiki/Telephone_numbers_in_Argentina for detailed explanations. + static { + String regex = + "0?(" + + " (" + + " 11|" + + " 2(" + + " 2(" + + " 02?|" + + " [13]|" + + " 2[13-79]|" + + " 4[1-6]|" + + " 5[2457]|" + + " 6[124-8]|" + + " 7[1-4]|" + + " 8[13-6]|" + + " 9[1267]" + + " )|" + + " 3(" + + " 02?|" + + " 1[467]|" + + " 2[03-6]|" + + " 3[13-8]|" + + " [49][2-6]|" + + " 5[2-8]|" + + " [67]" + + " )|" + + " 4(" + + " 7[3-578]|" + + " 9" + + " )|" + + " 6(" + + " [0136]|" + + " 2[24-6]|" + + " 4[6-8]?|" + + " 5[15-8]" + + " )|" + + " 80|" + + " 9(" + + " 0[1-3]|" + + " [19]|" + + " 2\\d|" + + " 3[1-6]|" + + " 4[02568]?|" + + " 5[2-4]|" + + " 6[2-46]|" + + " 72?|" + + " 8[23]?" + + " )" + + " )|" + + " 3(" + + " 3(" + + " 2[79]|" + + " 6|" + + " 8[2578]" + + " )|" + + " 4(" + + " 0[0-24-9]|" + + " [12]|" + + " 3[5-8]?|" + + " 4[24-7]|" + + " 5[4-68]?|" + + " 6[02-9]|" + + " 7[126]|" + + " 8[2379]?|" + + " 9[1-36-8]" + + " )|" + + " 5(" + + " 1|" + + " 2[1245]|" + + " 3[237]?|" + + " 4[1-46-9]|" + + " 6[2-4]|" + + " 7[1-6]|" + + " 8[2-5]?" + + " )|" + + " 6[24]|" + + " 7(" + + " [069]|" + + " 1[1568]|" + + " 2[15]|" + + " 3[145]|" + + " 4[13]|" + + " 5[14-8]|" + + " 7[2-57]|" + + " 8[126]" + + " )|" + + " 8(" + + " [01]|" + + " 2[15-7]|" + + " 3[2578]?|" + + " 4[13-6]|" + + " 5[4-8]?|" + + " 6[1-357-9]|" + + " 7[36-8]?|" + + " 8[5-8]?|" + + " 9[124]" + + " )" + + " )" + + " )?15" + + ").*"; + AR_DOMESTIC_CALL_MOBILE_NUMBER_PATTERN = Pattern.compile(regex.replaceAll("\\s+", "")); + } + + private final String countryCode; + + DialerPhoneNumberFormattingTextWatcher(String countryCode) { + super(countryCode); + this.countryCode = countryCode; + } + + @Override + public synchronized void afterTextChanged(Editable s) { + super.afterTextChanged(s); + + if (!"AR".equals(countryCode)) { + return; + } + + String rawNumber = getRawNumber(s); + + // As modifying the input will trigger another call to afterTextChanged(Editable), we must + // check whether the input's format has already been removed and return if it has + // been to avoid infinite recursion. + if (rawNumber.contentEquals(s)) { + return; + } + + Matcher matcher = AR_DOMESTIC_CALL_MOBILE_NUMBER_PATTERN.matcher(rawNumber); + if (matcher.matches()) { + s.replace(0, s.length(), rawNumber); + PhoneNumberUtils.addTtsSpan(s, 0 /* start */, s.length() /* endExclusive */); + } + } + + private static String getRawNumber(Editable s) { + StringBuilder rawNumberBuilder = new StringBuilder(); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (PhoneNumberUtils.isNonSeparator(c)) { + rawNumberBuilder.append(c); + } + } + + return rawNumberBuilder.toString(); } } } -- cgit v1.2.3 From 1306455ce5eb75c74790526ec3687293221a42ba Mon Sep 17 00:00:00 2001 From: twyen Date: Tue, 28 Nov 2017 15:27:11 -0800 Subject: Format number for SIM selection Bug: 69812286 Test: PhoneNumberHelperTest PiperOrigin-RevId: 177227451 Change-Id: I6cf53e50d17860f4374cbd9a94451bc0731905a4 --- .../widget/SelectPhoneAccountDialogFragment.java | 22 +++++++++++++++++++--- .../dialer/phonenumberutil/PhoneNumberHelper.java | 14 ++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'java') diff --git a/java/com/android/contacts/common/widget/SelectPhoneAccountDialogFragment.java b/java/com/android/contacts/common/widget/SelectPhoneAccountDialogFragment.java index e41a75b22..07891a069 100644 --- a/java/com/android/contacts/common/widget/SelectPhoneAccountDialogFragment.java +++ b/java/com/android/contacts/common/widget/SelectPhoneAccountDialogFragment.java @@ -25,11 +25,13 @@ import android.content.DialogInterface; import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; +import android.telephony.SubscriptionInfo; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; @@ -43,7 +45,10 @@ import android.widget.ListAdapter; import android.widget.TextView; import com.android.contacts.common.R; import com.android.contacts.common.compat.PhoneAccountCompat; -import com.android.contacts.common.compat.PhoneNumberUtilsCompat; +import com.android.dialer.location.GeoUtil; +import com.android.dialer.phonenumberutil.PhoneNumberHelper; +import com.android.dialer.telecom.TelecomUtil; +import com.google.common.base.Optional; import java.util.ArrayList; import java.util.List; @@ -319,8 +324,9 @@ public class SelectPhoneAccountDialogFragment extends DialogFragment { } else { holder.numberTextView.setVisibility(View.VISIBLE); holder.numberTextView.setText( - PhoneNumberUtilsCompat.createTtsSpannable( - account.getAddress().getSchemeSpecificPart())); + PhoneNumberHelper.formatNumberForDisplay( + account.getAddress().getSchemeSpecificPart(), + getCountryIso(getContext(), accountHandle))); } holder.imageView.setImageDrawable( PhoneAccountCompat.createIconDrawable(account, getContext())); @@ -339,6 +345,16 @@ public class SelectPhoneAccountDialogFragment extends DialogFragment { return rowView; } + private static String getCountryIso( + Context context, @NonNull PhoneAccountHandle phoneAccountHandle) { + Optional info = + TelecomUtil.getSubscriptionInfo(context, phoneAccountHandle); + if (!info.isPresent()) { + return GeoUtil.getCurrentCountryIso(context); + } + return info.get().getCountryIso().toUpperCase(); + } + private static final class ViewHolder { TextView labelTextView; diff --git a/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java b/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java index 40a338588..cdc06dead 100644 --- a/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java +++ b/java/com/android/dialer/phonenumberutil/PhoneNumberHelper.java @@ -26,6 +26,8 @@ import android.support.annotation.Nullable; import android.telecom.PhoneAccountHandle; import android.telephony.PhoneNumberUtils; import android.telephony.TelephonyManager; +import android.text.BidiFormatter; +import android.text.TextDirectionHeuristics; import android.text.TextUtils; import android.util.SparseIntArray; import com.android.dialer.common.Assert; @@ -337,6 +339,18 @@ public class PhoneNumberHelper { return formattedNumber != null ? formattedNumber : number; } + @Nullable + public static CharSequence formatNumberForDisplay( + @Nullable String number, @NonNull String countryIso) { + if (number == null) { + return null; + } + + return PhoneNumberUtils.createTtsSpannable( + BidiFormatter.getInstance() + .unicodeWrap(formatNumber(number, countryIso), TextDirectionHeuristics.LTR)); + } + /** * Determines if the specified number is actually a URI (i.e. a SIP address) rather than a regular * PSTN phone number, based on whether or not the number contains an "@" character. -- cgit v1.2.3 From 00d18e0efe89d7bbd4c69de6988ef6f9a62df782 Mon Sep 17 00:00:00 2001 From: mdooley Date: Tue, 28 Nov 2017 19:54:52 -0800 Subject: Switching to alarms and exponential backoff while polling for transcription result This cl switches the way asynchronous transcription tasks poll for their results. it has been observed that sometimes the server side transcription takes longer than expected (sometimes many minutes), so instead of blocking a thread for all that time this cl schedules an alarm for the next time to poll. it also uses an exponential backoff scheme to determine the poll times and increases the maximum total polling time from 20 seconds to 20 minutes. Bug: 66966157 Test: manual and unit tests PiperOrigin-RevId: 177257577 Change-Id: Ib2998f03cc418d5241ccffec71ba3945c9fe4cbc --- .../com/android/voicemail/impl/AndroidManifest.xml | 4 + .../impl/transcribe/GetTranscriptReceiver.java | 233 +++++++++++++++++++++ .../transcribe/TranscriptionConfigProvider.java | 21 +- .../impl/transcribe/TranscriptionTask.java | 99 +++++---- .../impl/transcribe/TranscriptionTaskAsync.java | 61 +----- 5 files changed, 315 insertions(+), 103 deletions(-) create mode 100644 java/com/android/voicemail/impl/transcribe/GetTranscriptReceiver.java (limited to 'java') diff --git a/java/com/android/voicemail/impl/AndroidManifest.xml b/java/com/android/voicemail/impl/AndroidManifest.xml index 53636092a..e7ab5818e 100644 --- a/java/com/android/voicemail/impl/AndroidManifest.xml +++ b/java/com/android/voicemail/impl/AndroidManifest.xml @@ -138,5 +138,9 @@ android:name="com.android.internal.telephony.CARRIER_VVM_PACKAGE_INSTALLED" /> + + + diff --git a/java/com/android/voicemail/impl/transcribe/GetTranscriptReceiver.java b/java/com/android/voicemail/impl/transcribe/GetTranscriptReceiver.java new file mode 100644 index 000000000..cc204ff53 --- /dev/null +++ b/java/com/android/voicemail/impl/transcribe/GetTranscriptReceiver.java @@ -0,0 +1,233 @@ +/* + * 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.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.SystemClock; +import android.support.annotation.Nullable; +import android.util.Pair; +import com.android.dialer.common.Assert; +import com.android.dialer.common.backoff.ExponentialBaseCalculator; +import com.android.dialer.common.concurrent.DialerExecutor.Worker; +import com.android.dialer.common.concurrent.DialerExecutorComponent; +import com.android.dialer.logging.DialerImpression; +import com.android.dialer.logging.Logger; +import com.android.voicemail.impl.VvmLog; +import com.android.voicemail.impl.transcribe.grpc.GetTranscriptResponseAsync; +import com.android.voicemail.impl.transcribe.grpc.TranscriptionClient; +import com.android.voicemail.impl.transcribe.grpc.TranscriptionClientFactory; +import com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest; +import com.google.internal.communications.voicemailtranscription.v1.TranscriptionStatus; + +/** + * This class uses the AlarmManager to poll for the result of a voicemail transcription request. + * Initially it waits for the estimated transcription time, and if the result is not available then + * it polls using an exponential backoff scheme. + */ +public class GetTranscriptReceiver extends BroadcastReceiver { + private static final String TAG = "GetTranscriptReceiver"; + static final String EXTRA_IS_INITIAL_ESTIMATED_WAIT = "extra_is_initial_estimated_wait"; + static final String EXTRA_VOICEMAIL_URI = "extra_voicemail_uri"; + static final String EXTRA_TRANSCRIPT_ID = "extra_transcript_id"; + static final String EXTRA_DELAY_MILLIS = "extra_delay_millis"; + static final String EXTRA_BASE_MULTIPLIER = "extra_base_multiplier"; + static final String EXTRA_REMAINING_ATTEMPTS = "extra_remaining_attempts"; + + // Schedule an initial alarm to begin checking for a voicemail transcription result. + static void beginPolling( + Context context, + Uri voicemailUri, + String transcriptId, + long estimatedTranscriptionTimeMillis, + TranscriptionConfigProvider configProvider) { + long initialDelayMillis = configProvider.getInitialGetTranscriptPollDelayMillis(); + long maxBackoffMillis = configProvider.getMaxGetTranscriptPollTimeMillis(); + int maxAttempts = configProvider.getMaxGetTranscriptPolls(); + double baseMultiplier = + ExponentialBaseCalculator.findBase(initialDelayMillis, maxBackoffMillis, maxAttempts); + Intent intent = + makeAlarmIntent( + context, voicemailUri, transcriptId, initialDelayMillis, baseMultiplier, maxAttempts); + // Add an extra to distinguish this initial estimated transcription wait from subsequent backoff + // waits + intent.putExtra(EXTRA_IS_INITIAL_ESTIMATED_WAIT, true); + VvmLog.i( + TAG, + String.format( + "beginPolling, check in %d millis, for: %s", + estimatedTranscriptionTimeMillis, transcriptId)); + scheduleAlarm(context, estimatedTranscriptionTimeMillis, intent); + } + + // Alarm fired, poll for transcription result on a background thread + @Override + public void onReceive(Context context, Intent intent) { + String transcriptId = intent.getStringExtra(EXTRA_TRANSCRIPT_ID); + VvmLog.i(TAG, "onReceive, for transcript id: " + transcriptId); + DialerExecutorComponent.get(context) + .dialerExecutorFactory() + .createNonUiTaskBuilder(new PollWorker(context)) + .onSuccess(this::onSuccess) + .onFailure(this::onFailure) + .build() + .executeParallel(intent); + } + + private void onSuccess(Void unused) { + VvmLog.i(TAG, "onSuccess"); + } + + private void onFailure(Throwable t) { + VvmLog.e(TAG, "onFailure", t); + } + + private static void scheduleAlarm(Context context, long delayMillis, Intent intent) { + PendingIntent alarmIntent = + PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + alarmMgr.set( + AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + delayMillis, + alarmIntent); + } + + private static Intent makeAlarmIntent( + Context context, + Uri voicemailUri, + String transcriptId, + long delayMillis, + double baseMultiplier, + int remainingAttempts) { + Intent intent = new Intent(context, GetTranscriptReceiver.class); + intent.putExtra(EXTRA_VOICEMAIL_URI, voicemailUri); + intent.putExtra(EXTRA_TRANSCRIPT_ID, transcriptId); + intent.putExtra(EXTRA_DELAY_MILLIS, delayMillis); + intent.putExtra(EXTRA_BASE_MULTIPLIER, baseMultiplier); + intent.putExtra(EXTRA_REMAINING_ATTEMPTS, remainingAttempts); + return intent; + } + + private static class PollWorker implements Worker { + private final Context context; + + PollWorker(Context context) { + this.context = context; + } + + @Override + public Void doInBackground(Intent intent) { + String transcriptId = intent.getStringExtra(EXTRA_TRANSCRIPT_ID); + VvmLog.i(TAG, "doInBackground, for transcript id: " + transcriptId); + Pair result = pollForTranscription(transcriptId); + if (result.first == null && result.second == null) { + // No result, try again if possible + Intent nextIntent = getNextAlarmIntent(intent); + if (nextIntent == null) { + VvmLog.i(TAG, "doInBackground, too many failures for: " + transcriptId); + result = new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY); + } else { + long nextDelayMillis = nextIntent.getLongExtra(EXTRA_DELAY_MILLIS, 0L); + VvmLog.i( + TAG, + String.format( + "doInBackground, check again in %d, for: %s", nextDelayMillis, transcriptId)); + scheduleAlarm(context, nextDelayMillis, nextIntent); + return null; + } + } + + // Got transcript or failed too many times + Uri voicemailUri = intent.getParcelableExtra(EXTRA_VOICEMAIL_URI); + TranscriptionDbHelper dbHelper = new TranscriptionDbHelper(context, voicemailUri); + TranscriptionTask.recordResult(context, result, dbHelper); + return null; + } + + private Pair pollForTranscription(String transcriptId) { + VvmLog.i(TAG, "pollForTranscription, transcript id: " + transcriptId); + GetTranscriptRequest request = getGetTranscriptRequest(transcriptId); + TranscriptionClientFactory factory = null; + try { + factory = getTranscriptionClientFactory(context); + TranscriptionClient client = factory.getClient(); + Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_POLL_REQUEST); + GetTranscriptResponseAsync response = client.sendGetTranscriptRequest(request); + if (response == null) { + VvmLog.i(TAG, "pollForTranscription, no transcription result."); + return new Pair<>(null, null); + } else if (response.isTranscribing()) { + VvmLog.i(TAG, "pollForTranscription, transcribing"); + return new Pair<>(null, null); + } else if (response.hasFatalError()) { + VvmLog.i(TAG, "pollForTranscription, fail. " + response.getErrorDescription()); + return new Pair<>(null, response.getTranscriptionStatus()); + } else { + VvmLog.i(TAG, "pollForTranscription, got transcription"); + return new Pair<>(response.getTranscript(), TranscriptionStatus.SUCCESS); + } + } finally { + if (factory != null) { + factory.shutdown(); + } + } + } + + private GetTranscriptRequest getGetTranscriptRequest(String transcriptionId) { + Assert.checkArgument(transcriptionId != null); + return GetTranscriptRequest.newBuilder().setTranscriptionId(transcriptionId).build(); + } + + private @Nullable Intent getNextAlarmIntent(Intent previous) { + int remainingAttempts = previous.getIntExtra(EXTRA_REMAINING_ATTEMPTS, 0); + double baseMultiplier = previous.getDoubleExtra(EXTRA_BASE_MULTIPLIER, 0); + long nextDelay = previous.getLongExtra(EXTRA_DELAY_MILLIS, 0); + if (!previous.getBooleanExtra(EXTRA_IS_INITIAL_ESTIMATED_WAIT, false)) { + // After waiting the estimated transcription time, start decrementing the remaining attempts + // and incrementing the backoff time delay + remainingAttempts--; + if (remainingAttempts <= 0) { + return null; + } + nextDelay = (long) (nextDelay * baseMultiplier); + } + return makeAlarmIntent( + context, + previous.getParcelableExtra(EXTRA_VOICEMAIL_URI), + previous.getStringExtra(EXTRA_TRANSCRIPT_ID), + nextDelay, + baseMultiplier, + remainingAttempts); + } + } + + private static TranscriptionClientFactory transcriptionClientFactoryForTesting; + + static void setTranscriptionClientFactoryForTesting(TranscriptionClientFactory factory) { + transcriptionClientFactoryForTesting = factory; + } + + private static TranscriptionClientFactory getTranscriptionClientFactory(Context context) { + if (transcriptionClientFactoryForTesting != null) { + return transcriptionClientFactoryForTesting; + } + return new TranscriptionClientFactory(context, new TranscriptionConfigProvider(context)); + } +} diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java b/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java index 98c8461f5..3d1755b64 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionConfigProvider.java @@ -18,6 +18,7 @@ package com.android.voicemail.impl.transcribe; import android.content.Context; import android.os.Build; import com.android.dialer.configprovider.ConfigProviderBindings; +import java.util.concurrent.TimeUnit; /** Provides configuration values needed to connect to the transcription server. */ public class TranscriptionConfigProvider { @@ -65,14 +66,24 @@ public class TranscriptionConfigProvider { .getLong("voicemail_transcription_max_transcription_retries", 2L); } - public long getMaxGetTranscriptPolls() { + public int getMaxGetTranscriptPolls() { + return (int) + ConfigProviderBindings.get(context) + .getLong("voicemail_transcription_max_get_transcript_polls", 20L); + } + + public long getInitialGetTranscriptPollDelayMillis() { return ConfigProviderBindings.get(context) - .getLong("voicemail_transcription_max_get_transcript_polls", 20L); + .getLong( + "voicemail_transcription_get_initial_transcript_poll_delay_millis", + TimeUnit.SECONDS.toMillis(1)); } - public long getGetTranscriptPollIntervalMillis() { + public long getMaxGetTranscriptPollTimeMillis() { return ConfigProviderBindings.get(context) - .getLong("voicemail_transcription_get_transcript_poll_interval_millis", 1000L); + .getLong( + "voicemail_transcription_get_max_transcript_poll_time_millis", + TimeUnit.MINUTES.toMillis(20)); } public boolean isVoicemailDonationAvailable() { @@ -97,6 +108,6 @@ public class TranscriptionConfigProvider { shouldUseSyncApi(), getMaxTranscriptionRetries(), getMaxGetTranscriptPolls(), - getGetTranscriptPollIntervalMillis()); + getMaxGetTranscriptPollTimeMillis()); } } diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java index 97cf89eef..d483f6fbd 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java @@ -21,7 +21,6 @@ import android.net.Uri; import android.support.annotation.MainThread; import android.support.annotation.VisibleForTesting; import android.telecom.PhoneAccountHandle; -import android.text.TextUtils; import android.util.Pair; import com.android.dialer.common.Assert; import com.android.dialer.common.concurrent.ThreadUtil; @@ -60,10 +59,10 @@ public abstract class TranscriptionTask implements Runnable { private final JobCallback callback; private final JobWorkItem workItem; private final TranscriptionClientFactory clientFactory; - private final Uri voicemailUri; + protected final Uri voicemailUri; protected final PhoneAccountHandle phoneAccountHandle; - private final TranscriptionDbHelper databaseHelper; protected final TranscriptionConfigProvider configProvider; + protected final TranscriptionDbHelper dbHelper; protected ByteString audioData; protected AudioFormat encoding; protected volatile boolean cancelled; @@ -86,7 +85,7 @@ public abstract class TranscriptionTask implements Runnable { this.voicemailUri = TranscriptionService.getVoicemailUri(workItem); this.phoneAccountHandle = TranscriptionService.getPhoneAccountHandle(workItem); this.configProvider = configProvider; - databaseHelper = new TranscriptionDbHelper(context, voicemailUri); + dbHelper = new TranscriptionDbHelper(context, voicemailUri); } @MainThread @@ -124,44 +123,7 @@ public abstract class TranscriptionTask implements Runnable { private void transcribeVoicemail() { VvmLog.i(TAG, "transcribeVoicemail"); - Pair pair = getTranscription(); - String transcript = pair.first; - TranscriptionStatus status = pair.second; - if (!TextUtils.isEmpty(transcript)) { - updateTranscriptionAndState(transcript, VoicemailCompat.TRANSCRIPTION_AVAILABLE); - VvmLog.i(TAG, "transcribeVoicemail, got response"); - Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_SUCCESS); - } else { - VvmLog.i(TAG, "transcribeVoicemail, transcription unsuccessful, " + status); - switch (status) { - case FAILED_NO_SPEECH_DETECTED: - updateTranscriptionAndState( - transcript, VoicemailCompat.TRANSCRIPTION_FAILED_NO_SPEECH_DETECTED); - Logger.get(context) - .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_NO_SPEECH_DETECTED); - break; - case FAILED_LANGUAGE_NOT_SUPPORTED: - updateTranscriptionAndState( - transcript, VoicemailCompat.TRANSCRIPTION_FAILED_LANGUAGE_NOT_SUPPORTED); - Logger.get(context) - .logImpression( - DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_LANGUAGE_NOT_SUPPORTED); - break; - case EXPIRED: - updateTranscriptionAndState(transcript, VoicemailCompat.TRANSCRIPTION_FAILED); - Logger.get(context) - .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_EXPIRED); - break; - default: - updateTranscriptionAndState( - transcript, - cancelled - ? VoicemailCompat.TRANSCRIPTION_NOT_STARTED - : VoicemailCompat.TRANSCRIPTION_FAILED); - Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_EMPTY); - break; - } - } + recordResult(context, getTranscription(), dbHelper, cancelled); } protected TranscriptionResponse sendRequest(Request request) { @@ -213,12 +175,57 @@ public abstract class TranscriptionTask implements Runnable { } } - private void updateTranscriptionAndState(String transcript, int newState) { - databaseHelper.setTranscriptionAndState(transcript, newState); + protected void updateTranscriptionState(int newState) { + dbHelper.setTranscriptionState(newState); + } + + protected void updateTranscriptionAndState(String transcript, int newState) { + dbHelper.setTranscriptionAndState(transcript, newState); } - private void updateTranscriptionState(int newState) { - databaseHelper.setTranscriptionState(newState); + static void recordResult( + Context context, Pair result, TranscriptionDbHelper dbHelper) { + recordResult(context, result, dbHelper, false); + } + + static void recordResult( + Context context, + Pair result, + TranscriptionDbHelper dbHelper, + boolean cancelled) { + if (result.first != null) { + VvmLog.i(TAG, "recordResult, got transcription"); + dbHelper.setTranscriptionAndState(result.first, VoicemailCompat.TRANSCRIPTION_AVAILABLE); + Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_SUCCESS); + } else if (result.second != null) { + VvmLog.i(TAG, "recordResult, failed to transcribe, reason: " + result.second); + switch (result.second) { + case FAILED_NO_SPEECH_DETECTED: + dbHelper.setTranscriptionState(VoicemailCompat.TRANSCRIPTION_FAILED_NO_SPEECH_DETECTED); + Logger.get(context) + .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_NO_SPEECH_DETECTED); + break; + case FAILED_LANGUAGE_NOT_SUPPORTED: + dbHelper.setTranscriptionState( + VoicemailCompat.TRANSCRIPTION_FAILED_LANGUAGE_NOT_SUPPORTED); + Logger.get(context) + .logImpression( + DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_LANGUAGE_NOT_SUPPORTED); + break; + case EXPIRED: + dbHelper.setTranscriptionState(VoicemailCompat.TRANSCRIPTION_FAILED); + Logger.get(context) + .logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_EXPIRED); + break; + default: + dbHelper.setTranscriptionState( + cancelled + ? VoicemailCompat.TRANSCRIPTION_NOT_STARTED + : VoicemailCompat.TRANSCRIPTION_FAILED); + Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_RESPONSE_EMPTY); + break; + } + } } private boolean readAndValidateAudioFile() { diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java index 808bf0f87..7abf2484e 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java @@ -19,17 +19,13 @@ import android.app.job.JobWorkItem; import android.content.Context; import android.support.annotation.VisibleForTesting; import android.util.Pair; -import com.android.dialer.common.Assert; import com.android.dialer.logging.DialerImpression; -import com.android.dialer.logging.Logger; import com.android.voicemail.VoicemailComponent; import com.android.voicemail.impl.VvmLog; import com.android.voicemail.impl.transcribe.TranscriptionService.JobCallback; -import com.android.voicemail.impl.transcribe.grpc.GetTranscriptResponseAsync; import com.android.voicemail.impl.transcribe.grpc.TranscriptionClientFactory; import com.android.voicemail.impl.transcribe.grpc.TranscriptionResponseAsync; import com.google.internal.communications.voicemailtranscription.v1.DonationPreference; -import com.google.internal.communications.voicemailtranscription.v1.GetTranscriptRequest; import com.google.internal.communications.voicemailtranscription.v1.TranscribeVoicemailAsyncRequest; import com.google.internal.communications.voicemailtranscription.v1.TranscriptionStatus; @@ -73,8 +69,15 @@ public class TranscriptionTaskAsync extends TranscriptionTask { VvmLog.i(TAG, "getTranscription, failed to upload voicemail."); return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY); } else { - waitForTranscription(uploadResponse); - return pollForTranscription(uploadResponse); + VvmLog.i(TAG, "getTranscription, begin polling for result."); + GetTranscriptReceiver.beginPolling( + context, + voicemailUri, + uploadResponse.getTranscriptionId(), + uploadResponse.getEstimatedWaitMillis(), + configProvider); + // This indicates that the result is not available yet + return new Pair<>(null, null); } } @@ -83,45 +86,6 @@ public class TranscriptionTaskAsync extends TranscriptionTask { return DialerImpression.Type.VVM_TRANSCRIPTION_REQUEST_SENT_ASYNC; } - private static void waitForTranscription(TranscriptionResponseAsync uploadResponse) { - long millis = uploadResponse.getEstimatedWaitMillis(); - VvmLog.i(TAG, "waitForTranscription, " + millis + " millis"); - sleep(millis); - } - - private Pair pollForTranscription( - TranscriptionResponseAsync uploadResponse) { - VvmLog.i(TAG, "pollForTranscription"); - GetTranscriptRequest request = getGetTranscriptRequest(uploadResponse); - for (int i = 0; i < configProvider.getMaxGetTranscriptPolls(); i++) { - if (cancelled) { - VvmLog.i(TAG, "pollForTranscription, cancelled."); - return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY); - } - Logger.get(context).logImpression(DialerImpression.Type.VVM_TRANSCRIPTION_POLL_REQUEST); - GetTranscriptResponseAsync response = - (GetTranscriptResponseAsync) - sendRequest((client) -> client.sendGetTranscriptRequest(request)); - if (cancelled) { - VvmLog.i(TAG, "pollForTranscription, cancelled."); - return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY); - } else if (response == null) { - VvmLog.i(TAG, "pollForTranscription, no transcription result."); - } else if (response.isTranscribing()) { - VvmLog.i(TAG, "pollForTranscription, poll count: " + (i + 1)); - } else if (response.hasFatalError()) { - VvmLog.i(TAG, "pollForTranscription, fail. " + response.getErrorDescription()); - return new Pair<>(null, response.getTranscriptionStatus()); - } else { - VvmLog.i(TAG, "pollForTranscription, got transcription"); - return new Pair<>(response.getTranscript(), TranscriptionStatus.SUCCESS); - } - sleep(configProvider.getGetTranscriptPollIntervalMillis()); - } - VvmLog.i(TAG, "pollForTranscription, timed out."); - return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY); - } - @VisibleForTesting TranscribeVoicemailAsyncRequest getUploadRequest() { TranscribeVoicemailAsyncRequest.Builder builder = @@ -145,11 +109,4 @@ public class TranscriptionTaskAsync extends TranscriptionTask { .getVoicemailClient() .isVoicemailDonationEnabled(context, phoneAccountHandle); } - - private GetTranscriptRequest getGetTranscriptRequest(TranscriptionResponseAsync uploadResponse) { - Assert.checkArgument(uploadResponse.getTranscriptionId() != null); - return GetTranscriptRequest.newBuilder() - .setTranscriptionId(uploadResponse.getTranscriptionId()) - .build(); - } } -- cgit v1.2.3 From b9ca335dbbc3325643549f6fbf2dac06a5502d26 Mon Sep 17 00:00:00 2001 From: mdooley Date: Wed, 29 Nov 2017 08:53:31 -0800 Subject: Delete voicemails when disabling visual voicemail For privacy reasons we now delete all voicemails and transcriptions when the user disables visual voicemail, (after confirming that they want to do this via a dialog). Note: we only delete voicemails whose source package is the google dialer. Note: the voicemails will be re-downloaded and re-transcribed if the user re-enables visual voicemail Bug: 69323147 Test: manual and unit test PiperOrigin-RevId: 177315139 Change-Id: Ie04496dc5960b485794733fbf74f7f704e806023 --- .../settings/VoicemailSettingsFragment.java | 61 ++++++++++++++++++---- .../voicemail/settings/res/values/strings.xml | 7 +++ .../impl/settings/VisualVoicemailSettingsUtil.java | 34 ++++++++++++ .../impl/transcribe/TranscriptionRatingHelper.java | 3 +- .../impl/transcribe/TranscriptionTask.java | 2 +- .../impl/transcribe/TranscriptionTaskAsync.java | 10 +++- .../impl/transcribe/TranscriptionUtils.java | 9 +++- .../transcribe/grpc/TranscriptionResponse.java | 5 ++ .../grpc/TranscriptionResponseAsync.java | 5 ++ .../transcribe/grpc/TranscriptionResponseSync.java | 5 ++ .../impl/utils/VoicemailDatabaseUtil.java | 10 ++++ 11 files changed, 137 insertions(+), 14 deletions(-) (limited to 'java') diff --git a/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java index aaa1e150d..5ae26f5f7 100644 --- a/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java +++ b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java @@ -14,6 +14,8 @@ package com.android.dialer.voicemail.settings; import android.annotation.TargetApi; +import android.app.AlertDialog; +import android.content.DialogInterface; import android.content.Intent; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -226,16 +228,13 @@ public class VoicemailSettingsFragment extends PreferenceFragment LogUtil.d(TAG, "onPreferenceChange: \"" + preference + "\" changed to \"" + objValue + "\""); if (preference.getKey().equals(voicemailVisualVoicemail.getKey())) { boolean isEnabled = (boolean) objValue; - voicemailClient.setVoicemailEnabled(getContext(), phoneAccountHandle, isEnabled); - - if (isEnabled) { - Logger.get(getContext()).logImpression(DialerImpression.Type.VVM_USER_ENABLED_IN_SETTINGS); + if (!isEnabled) { + showDisableConfirmationDialog(); + // Don't let the preference setting proceed. + return false; } else { - Logger.get(getContext()).logImpression(DialerImpression.Type.VVM_USER_DISABLED_IN_SETTINGS); + updateVoicemailEnabled(true); } - - updateChangePin(); - updateDonateVoicemail(); } else if (preference.getKey().equals(autoArchiveSwitchPreference.getKey())) { logArchiveToggle((boolean) objValue); voicemailClient.setVoicemailArchiveEnabled( @@ -246,10 +245,24 @@ public class VoicemailSettingsFragment extends PreferenceFragment getContext(), phoneAccountHandle, (boolean) objValue); } - // Always let the preference setting proceed. + // Let the preference setting proceed. return true; } + private void updateVoicemailEnabled(boolean isEnabled) { + voicemailClient.setVoicemailEnabled(getContext(), phoneAccountHandle, isEnabled); + voicemailVisualVoicemail.setChecked(isEnabled); + + if (isEnabled) { + Logger.get(getContext()).logImpression(DialerImpression.Type.VVM_USER_ENABLED_IN_SETTINGS); + } else { + Logger.get(getContext()).logImpression(DialerImpression.Type.VVM_USER_DISABLED_IN_SETTINGS); + } + + updateChangePin(); + updateDonateVoicemail(); + } + private void updateChangePin() { if (!voicemailClient.isVoicemailEnabled(getContext(), phoneAccountHandle)) { voicemailChangePinPreference.setSummary( @@ -305,4 +318,34 @@ public class VoicemailSettingsFragment extends PreferenceFragment .putExtra(Settings.EXTRA_CHANNEL_ID, channelId) .putExtra(Settings.EXTRA_APP_PACKAGE, getContext().getPackageName()); } + + private void showDisableConfirmationDialog() { + LogUtil.i(TAG, "showDisableConfirmationDialog"); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(R.string.confirm_disable_voicemail_dialog_title); + builder.setMessage(R.string.confirm_disable_voicemail_dialog_message); + builder.setPositiveButton( + R.string.confirm_disable_voicemail_accept_dialog_label, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + LogUtil.i(TAG, "showDisableConfirmationDialog, confirmed"); + updateVoicemailEnabled(false); + dialog.dismiss(); + } + }); + + builder.setNegativeButton( + android.R.string.cancel, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + LogUtil.i(TAG, "showDisableConfirmationDialog, cancelled"); + dialog.dismiss(); + } + }); + + builder.setCancelable(true); + builder.show(); + } } diff --git a/java/com/android/dialer/voicemail/settings/res/values/strings.xml b/java/com/android/dialer/voicemail/settings/res/values/strings.xml index 4e502b488..10fa459ff 100644 --- a/java/com/android/dialer/voicemail/settings/res/values/strings.xml +++ b/java/com/android/dialer/voicemail/settings/res/values/strings.xml @@ -118,4 +118,11 @@ Let Google review your voicemail messages to improve transcription quality + + Turn off visual voicemail + + This will delete any voicemail and Google transcripts stored within this app. Your carrier may keep its own copies. + + TURN OFF + diff --git a/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java b/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java index 61d76194c..e42d56938 100644 --- a/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java +++ b/java/com/android/voicemail/impl/settings/VisualVoicemailSettingsUtil.java @@ -19,11 +19,14 @@ import android.content.Context; import android.support.annotation.VisibleForTesting; import android.telecom.PhoneAccountHandle; import com.android.dialer.common.Assert; +import com.android.dialer.common.concurrent.DialerExecutor.Worker; +import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.voicemail.VoicemailComponent; import com.android.voicemail.impl.OmtpVvmCarrierConfigHelper; import com.android.voicemail.impl.VisualVoicemailPreferences; import com.android.voicemail.impl.VvmLog; import com.android.voicemail.impl.sync.VvmAccountManager; +import com.android.voicemail.impl.utils.VoicemailDatabaseUtil; /** Save whether or not a particular account is enabled in shared to be retrieved later. */ public class VisualVoicemailSettingsUtil { @@ -45,9 +48,40 @@ public class VisualVoicemailSettingsUtil { } else { VvmAccountManager.removeAccount(context, phoneAccount); config.startDeactivation(); + // Remove all voicemails from the database + DialerExecutorComponent.get(context) + .dialerExecutorFactory() + .createNonUiTaskBuilder(new VoicemailDeleteWorker(context)) + .onSuccess(VisualVoicemailSettingsUtil::onSuccess) + .onFailure(VisualVoicemailSettingsUtil::onFailure) + .build() + .executeParallel(null); } } + private static class VoicemailDeleteWorker implements Worker { + private final Context context; + + VoicemailDeleteWorker(Context context) { + this.context = context; + } + + @Override + public Void doInBackground(Void unused) { + int deleted = VoicemailDatabaseUtil.deleteAll(context); + VvmLog.i("VisualVoicemailSettingsUtil.doInBackground", "deleted " + deleted + " voicemails"); + return null; + } + } + + private static void onSuccess(Void unused) { + VvmLog.i("VisualVoicemailSettingsUtil.onSuccess", "delete voicemails"); + } + + private static void onFailure(Throwable t) { + VvmLog.e("VisualVoicemailSettingsUtil.onFailure", "delete voicemails", t); + } + public static void setArchiveEnabled( Context context, PhoneAccountHandle phoneAccount, boolean isEnabled) { Assert.checkArgument( diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionRatingHelper.java b/java/com/android/voicemail/impl/transcribe/TranscriptionRatingHelper.java index 1cafacecf..b721ba5b0 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionRatingHelper.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionRatingHelper.java @@ -85,7 +85,8 @@ public class TranscriptionRatingHelper { private SendTranscriptionFeedbackRequest getFeedbackRequest() { ByteString audioData = TranscriptionUtils.getAudioData(context, voicemailUri); - String voicemailId = TranscriptionUtils.getFingerprintFor(audioData); + String salt = voicemailUri.toString(); + String voicemailId = TranscriptionUtils.getFingerprintFor(audioData, salt); TranscriptionRating rating = TranscriptionRating.newBuilder() .setTranscriptionId(voicemailId) diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java index d483f6fbd..ca3320f28 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTask.java @@ -55,10 +55,10 @@ import com.google.protobuf.ByteString; public abstract class TranscriptionTask implements Runnable { private static final String TAG = "TranscriptionTask"; - protected final Context context; private final JobCallback callback; private final JobWorkItem workItem; private final TranscriptionClientFactory clientFactory; + protected final Context context; protected final Uri voicemailUri; protected final PhoneAccountHandle phoneAccountHandle; protected final TranscriptionConfigProvider configProvider; diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java index 7abf2484e..bb7aa5f38 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionTaskAsync.java @@ -68,6 +68,9 @@ public class TranscriptionTaskAsync extends TranscriptionTask { } else if (uploadResponse == null) { VvmLog.i(TAG, "getTranscription, failed to upload voicemail."); return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY); + } else if (uploadResponse.getTranscriptionId() == null) { + VvmLog.i(TAG, "getTranscription, upload error: " + uploadResponse.status); + return new Pair<>(null, TranscriptionStatus.FAILED_NO_RETRY); } else { VvmLog.i(TAG, "getTranscription, begin polling for result."); GetTranscriptReceiver.beginPolling( @@ -98,7 +101,12 @@ public class TranscriptionTaskAsync extends TranscriptionTask { // available (because rating donating voicemails requires locally generated voicemail ids). if (configProvider.useClientGeneratedVoicemailIds() || configProvider.isVoicemailDonationAvailable()) { - builder.setTranscriptionId(TranscriptionUtils.getFingerprintFor(audioData)); + // The server currently can't handle repeated transcription id's so if we add the Uri to the + // fingerprint (which contains the voicemail id) which is different each time a voicemail is + // downloaded. If this becomes a problem then it should be possible to change the server + // behavior to allow id's to be re-used, a bug + String salt = voicemailUri.toString(); + builder.setTranscriptionId(TranscriptionUtils.getFingerprintFor(audioData, salt)); } return builder.build(); } diff --git a/java/com/android/voicemail/impl/transcribe/TranscriptionUtils.java b/java/com/android/voicemail/impl/transcribe/TranscriptionUtils.java index 36b1400be..3bd14731f 100644 --- a/java/com/android/voicemail/impl/transcribe/TranscriptionUtils.java +++ b/java/com/android/voicemail/impl/transcribe/TranscriptionUtils.java @@ -18,6 +18,8 @@ package com.android.voicemail.impl.transcribe; import android.annotation.TargetApi; import android.content.Context; import android.net.Uri; +import android.os.Build.VERSION_CODES; +import android.support.annotation.Nullable; import android.util.Base64; import com.android.dialer.common.Assert; import com.google.internal.communications.voicemailtranscription.v1.AudioFormat; @@ -47,11 +49,14 @@ public class TranscriptionUtils { : AudioFormat.AUDIO_FORMAT_UNSPECIFIED; } - @TargetApi(android.os.Build.VERSION_CODES.O) - static String getFingerprintFor(ByteString data) { + @TargetApi(VERSION_CODES.O) + static String getFingerprintFor(ByteString data, @Nullable String salt) { Assert.checkArgument(data != null); try { MessageDigest md = MessageDigest.getInstance("MD5"); + if (salt != null) { + md.update(salt.getBytes()); + } byte[] md5Bytes = md.digest(data.toByteArray()); return Base64.encodeToString(md5Bytes, Base64.DEFAULT); } catch (NoSuchAlgorithmException e) { diff --git a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponse.java b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponse.java index f0823de32..ae4796dea 100644 --- a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponse.java +++ b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponse.java @@ -50,4 +50,9 @@ public abstract class TranscriptionResponse { return false; } + + @Override + public String toString() { + return "status: " + status; + } } diff --git a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseAsync.java b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseAsync.java index 38b463053..bd5679407 100644 --- a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseAsync.java +++ b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseAsync.java @@ -50,4 +50,9 @@ public class TranscriptionResponseAsync extends TranscriptionResponse { } return 0; } + + @Override + public String toString() { + return super.toString() + ", response: " + response; + } } diff --git a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseSync.java b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseSync.java index d2e2e218c..382bd1a97 100644 --- a/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseSync.java +++ b/java/com/android/voicemail/impl/transcribe/grpc/TranscriptionResponseSync.java @@ -40,4 +40,9 @@ public class TranscriptionResponseSync extends TranscriptionResponse { public @Nullable String getTranscript() { return (response != null) ? response.getTranscript() : null; } + + @Override + public String toString() { + return super.toString() + ", response: " + response; + } } diff --git a/java/com/android/voicemail/impl/utils/VoicemailDatabaseUtil.java b/java/com/android/voicemail/impl/utils/VoicemailDatabaseUtil.java index 711d6a8a4..ef5447d32 100644 --- a/java/com/android/voicemail/impl/utils/VoicemailDatabaseUtil.java +++ b/java/com/android/voicemail/impl/utils/VoicemailDatabaseUtil.java @@ -57,6 +57,16 @@ public class VoicemailDatabaseUtil { return voicemails.size(); } + /** + * Delete all the voicemails whose source_package field matches this package + * + * @return the number of voicemails deleted + */ + public static int deleteAll(Context context) { + ContentResolver contentResolver = context.getContentResolver(); + return contentResolver.delete(Voicemails.buildSourceUri(context.getPackageName()), null, null); + } + /** Maps structured {@link Voicemail} to {@link ContentValues} in content provider. */ private static ContentValues getContentValues(Voicemail voicemail) { ContentValues contentValues = new ContentValues(); -- cgit v1.2.3