diff options
author | Eric Erfanian <erfanian@google.com> | 2017-02-22 16:32:36 -0800 |
---|---|---|
committer | Eric Erfanian <erfanian@google.com> | 2017-03-01 09:56:52 -0800 |
commit | ccca31529c07970e89419fb85a9e8153a5396838 (patch) | |
tree | a7034c0a01672b97728c13282a2672771cd28baa /java/com/android/voicemailomtp/settings/VoicemailChangePinActivity.java | |
parent | e7ae4624ba6f25cb8e648db74e0d64c0113a16ba (diff) |
Update dialer sources.
Test: Built package and system image.
This change clobbers the old source, and is an export
from an internal Google repository.
The internal repository was forked form Android in March,
and this change includes modifications since then, to
near the v8 release.
Since the fork, we've moved code from monolithic to independent modules. In addition,
we've switched to Blaze/Bazel as the build sysetm. This export, however, still uses make.
New dependencies have been added:
- Dagger
- Auto-Value
- Glide
- Libshortcutbadger
Going forward, development will still be in Google3, and the Gerrit release
will become an automated export, with the next drop happening in ~ two weeks.
Android.mk includes local modifications from ToT.
Abridged changelog:
Bug fixes
● Not able to mute, add a call when using Phone app in multiwindow mode
● Double tap on keypad triggering multiple key and tones
● Reported spam numbers not showing as spam in the call log
● Crash when user tries to block number while Phone app is not set as default
● Crash when user picks a number from search auto-complete list
Visual Voicemail (VVM) improvements
● Share Voicemail audio via standard exporting mechanisms that support file attachment
(email, MMS, etc.)
● Make phone number, email and web sites in VVM transcript clickable
● Set PIN before declining VVM Terms of Service {Carrier}
● Set client type for outbound visual voicemail SMS {Carrier}
New incoming call and incall UI on older devices
(Android M)
● Updated Phone app icon
● New incall UI (large buttons, button labels)
● New and animated Answer/Reject gestures
Accessibility
● Add custom answer/decline call buttons on answer screen for touch exploration
accessibility services
● Increase size of touch target
● Add verbal feedback when a Voicemail fails to load
● Fix pressing of Phone buttons while in a phone call using Switch Access
● Fix selecting and opening contacts in talkback mode
● Split focus for ‘Learn More’ link in caller id & spam to help distinguish similar text
Other
● Backup & Restore for App Preferences
● Prompt user to enable Wi-Fi calling if the call ends due to out of service and Wi-Fi is
connected
● Rename “Dialpad” to “Keypad”
● Show "Private number" for restricted calls
● Delete unused items (vcard, add contact, call history) from Phone menu
Change-Id: I2a7e53532a24c21bf308bf0a6d178d7ddbca4958
Diffstat (limited to 'java/com/android/voicemailomtp/settings/VoicemailChangePinActivity.java')
-rw-r--r-- | java/com/android/voicemailomtp/settings/VoicemailChangePinActivity.java | 634 |
1 files changed, 634 insertions, 0 deletions
diff --git a/java/com/android/voicemailomtp/settings/VoicemailChangePinActivity.java b/java/com/android/voicemailomtp/settings/VoicemailChangePinActivity.java new file mode 100644 index 000000000..e679e9970 --- /dev/null +++ b/java/com/android/voicemailomtp/settings/VoicemailChangePinActivity.java @@ -0,0 +1,634 @@ +/* + * Copyright (C) 2016 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.voicemailomtp.settings; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnDismissListener; +import android.net.Network; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.support.annotation.Nullable; +import android.telecom.PhoneAccountHandle; +import android.text.Editable; +import android.text.InputFilter; +import android.text.InputFilter.LengthFilter; +import android.text.TextWatcher; +import android.view.KeyEvent; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; +import android.widget.Toast; +import com.android.voicemailomtp.OmtpConstants; +import com.android.voicemailomtp.OmtpConstants.ChangePinResult; +import com.android.voicemailomtp.OmtpEvents; +import com.android.voicemailomtp.OmtpVvmCarrierConfigHelper; +import com.android.voicemailomtp.R; +import com.android.voicemailomtp.VisualVoicemailPreferences; +import com.android.voicemailomtp.VoicemailStatus; +import com.android.voicemailomtp.VvmLog; +import com.android.voicemailomtp.imap.ImapHelper; +import com.android.voicemailomtp.imap.ImapHelper.InitializingException; +import com.android.voicemailomtp.mail.MessagingException; +import com.android.voicemailomtp.sync.VvmNetworkRequestCallback; + +/** + * Dialog to change the voicemail PIN. The TUI (Telephony User Interface) PIN is used when accessing + * traditional voicemail through phone call. The intent to launch this activity must contain {@link + * #EXTRA_PHONE_ACCOUNT_HANDLE} + */ +@TargetApi(VERSION_CODES.CUR_DEVELOPMENT) +public class VoicemailChangePinActivity extends Activity + implements OnClickListener, OnEditorActionListener, TextWatcher { + + private static final String TAG = "VmChangePinActivity"; + + public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "extra_phone_account_handle"; + + private static final String KEY_DEFAULT_OLD_PIN = "default_old_pin"; + + private static final int MESSAGE_HANDLE_RESULT = 1; + + private PhoneAccountHandle mPhoneAccountHandle; + private OmtpVvmCarrierConfigHelper mConfig; + + private int mPinMinLength; + private int mPinMaxLength; + + private State mUiState = State.Initial; + private String mOldPin; + private String mFirstPin; + + private ProgressDialog mProgressDialog; + + private TextView mHeaderText; + private TextView mHintText; + private TextView mErrorText; + private EditText mPinEntry; + private Button mCancelButton; + private Button mNextButton; + + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == MESSAGE_HANDLE_RESULT) { + mUiState.handleResult(VoicemailChangePinActivity.this, message.arg1); + } + } + }; + + private enum State { + /** + * Empty state to handle initial state transition. Will immediately switch into {@link + * #VerifyOldPin} if a default PIN has been set by the OMTP client, or {@link #EnterOldPin} + * if not. + */ + Initial, + /** + * Prompt the user to enter old PIN. The PIN will be verified with the server before + * proceeding to {@link #EnterNewPin}. + */ + EnterOldPin { + @Override + public void onEnter(VoicemailChangePinActivity activity) { + activity.setHeader(R.string.change_pin_enter_old_pin_header); + activity.mHintText.setText(R.string.change_pin_enter_old_pin_hint); + activity.mNextButton.setText(R.string.change_pin_continue_label); + activity.mErrorText.setText(null); + } + + @Override + public void onInputChanged(VoicemailChangePinActivity activity) { + activity.setNextEnabled(activity.getCurrentPasswordInput().length() > 0); + } + + + @Override + public void handleNext(VoicemailChangePinActivity activity) { + activity.mOldPin = activity.getCurrentPasswordInput(); + activity.verifyOldPin(); + } + + @Override + public void handleResult(VoicemailChangePinActivity activity, + @ChangePinResult int result) { + if (result == OmtpConstants.CHANGE_PIN_SUCCESS) { + activity.updateState(State.EnterNewPin); + } else { + CharSequence message = activity.getChangePinResultMessage(result); + activity.showError(message); + activity.mPinEntry.setText(""); + } + } + }, + /** + * The default old PIN is found. Show a blank screen while verifying with the server to make + * sure the PIN is still valid. If the PIN is still valid, proceed to {@link #EnterNewPin}. + * If not, the user probably changed the PIN through other means, proceed to {@link + * #EnterOldPin}. If any other issue caused the verifying to fail, show an error and exit. + */ + VerifyOldPin { + @Override + public void onEnter(VoicemailChangePinActivity activity) { + activity.findViewById(android.R.id.content).setVisibility(View.INVISIBLE); + activity.verifyOldPin(); + } + + @Override + public void handleResult(final VoicemailChangePinActivity activity, + @ChangePinResult int result) { + if (result == OmtpConstants.CHANGE_PIN_SUCCESS) { + activity.updateState(State.EnterNewPin); + } else if (result == OmtpConstants.CHANGE_PIN_SYSTEM_ERROR) { + activity.getWindow().setSoftInputMode( + WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + activity.showError(activity.getString(R.string.change_pin_system_error), + new OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + activity.finish(); + } + }); + } else { + VvmLog.e(TAG, "invalid default old PIN: " + activity + .getChangePinResultMessage(result)); + // If the default old PIN is rejected by the server, the PIN is probably changed + // through other means, or the generated pin is invalid + // Wipe the default old PIN so the old PIN input box will be shown to the user + // on the next time. + setDefaultOldPIN(activity, activity.mPhoneAccountHandle, null); + activity.handleOmtpEvent(OmtpEvents.CONFIG_PIN_SET); + activity.updateState(State.EnterOldPin); + } + } + + @Override + public void onLeave(VoicemailChangePinActivity activity) { + activity.findViewById(android.R.id.content).setVisibility(View.VISIBLE); + } + }, + /** + * Let the user enter the new PIN and validate the format. Only length is enforced, PIN + * strength check relies on the server. After a valid PIN is entered, proceed to {@link + * #ConfirmNewPin} + */ + EnterNewPin { + @Override + public void onEnter(VoicemailChangePinActivity activity) { + activity.mHeaderText.setText(R.string.change_pin_enter_new_pin_header); + activity.mNextButton.setText(R.string.change_pin_continue_label); + activity.mHintText.setText( + activity.getString(R.string.change_pin_enter_new_pin_hint, + activity.mPinMinLength, activity.mPinMaxLength)); + } + + @Override + public void onInputChanged(VoicemailChangePinActivity activity) { + String password = activity.getCurrentPasswordInput(); + if (password.length() == 0) { + activity.setNextEnabled(false); + return; + } + CharSequence error = activity.validatePassword(password); + if (error != null) { + activity.mErrorText.setText(error); + activity.setNextEnabled(false); + } else { + activity.mErrorText.setText(null); + activity.setNextEnabled(true); + } + } + + @Override + public void handleNext(VoicemailChangePinActivity activity) { + CharSequence errorMsg; + errorMsg = activity.validatePassword(activity.getCurrentPasswordInput()); + if (errorMsg != null) { + activity.showError(errorMsg); + return; + } + activity.mFirstPin = activity.getCurrentPasswordInput(); + activity.updateState(State.ConfirmNewPin); + } + }, + /** + * Let the user type in the same PIN again to avoid typos. If the PIN matches then perform a + * PIN change to the server. Finish the activity if succeeded. Return to {@link + * #EnterOldPin} if the old PIN is rejected, {@link #EnterNewPin} for other failure. + */ + ConfirmNewPin { + @Override + public void onEnter(VoicemailChangePinActivity activity) { + activity.mHeaderText.setText(R.string.change_pin_confirm_pin_header); + activity.mHintText.setText(null); + activity.mNextButton.setText(R.string.change_pin_ok_label); + } + + @Override + public void onInputChanged(VoicemailChangePinActivity activity) { + if (activity.getCurrentPasswordInput().length() == 0) { + activity.setNextEnabled(false); + return; + } + if (activity.getCurrentPasswordInput().equals(activity.mFirstPin)) { + activity.setNextEnabled(true); + activity.mErrorText.setText(null); + } else { + activity.setNextEnabled(false); + activity.mErrorText.setText(R.string.change_pin_confirm_pins_dont_match); + } + } + + @Override + public void handleResult(VoicemailChangePinActivity activity, + @ChangePinResult int result) { + if (result == OmtpConstants.CHANGE_PIN_SUCCESS) { + // If the PIN change succeeded we no longer know what the old (current) PIN is. + // Wipe the default old PIN so the old PIN input box will be shown to the user + // on the next time. + setDefaultOldPIN(activity, activity.mPhoneAccountHandle, null); + activity.handleOmtpEvent(OmtpEvents.CONFIG_PIN_SET); + + activity.finish(); + + Toast.makeText(activity, activity.getString(R.string.change_pin_succeeded), + Toast.LENGTH_SHORT).show(); + } else { + CharSequence message = activity.getChangePinResultMessage(result); + VvmLog.i(TAG, "Change PIN failed: " + message); + activity.showError(message); + if (result == OmtpConstants.CHANGE_PIN_MISMATCH) { + // Somehow the PIN has changed, prompt to enter the old PIN again. + activity.updateState(State.EnterOldPin); + } else { + // The new PIN failed to fulfil other restrictions imposed by the server. + activity.updateState(State.EnterNewPin); + } + + } + + } + + @Override + public void handleNext(VoicemailChangePinActivity activity) { + activity.processPinChange(activity.mOldPin, activity.mFirstPin); + } + }; + + /** + * The activity has switched from another state to this one. + */ + public void onEnter(VoicemailChangePinActivity activity) { + // Do nothing + } + + /** + * The user has typed something into the PIN input field. Also called after {@link + * #onEnter(VoicemailChangePinActivity)} + */ + public void onInputChanged(VoicemailChangePinActivity activity) { + // Do nothing + } + + /** + * The asynchronous call to change the PIN on the server has returned. + */ + public void handleResult(VoicemailChangePinActivity activity, @ChangePinResult int result) { + // Do nothing + } + + /** + * The user has pressed the "next" button. + */ + public void handleNext(VoicemailChangePinActivity activity) { + // Do nothing + } + + /** + * The activity has switched from this state to another one. + */ + public void onLeave(VoicemailChangePinActivity activity) { + // Do nothing + } + + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mPhoneAccountHandle = getIntent().getParcelableExtra(EXTRA_PHONE_ACCOUNT_HANDLE); + mConfig = new OmtpVvmCarrierConfigHelper(this, mPhoneAccountHandle); + setContentView(R.layout.voicemail_change_pin); + setTitle(R.string.change_pin_title); + + readPinLength(); + + View view = findViewById(android.R.id.content); + + mCancelButton = (Button) view.findViewById(R.id.cancel_button); + mCancelButton.setOnClickListener(this); + mNextButton = (Button) view.findViewById(R.id.next_button); + mNextButton.setOnClickListener(this); + + mPinEntry = (EditText) view.findViewById(R.id.pin_entry); + mPinEntry.setOnEditorActionListener(this); + mPinEntry.addTextChangedListener(this); + if (mPinMaxLength != 0) { + mPinEntry.setFilters(new InputFilter[]{new LengthFilter(mPinMaxLength)}); + } + + + mHeaderText = (TextView) view.findViewById(R.id.headerText); + mHintText = (TextView) view.findViewById(R.id.hintText); + mErrorText = (TextView) view.findViewById(R.id.errorText); + + if (isDefaultOldPinSet(this, mPhoneAccountHandle)) { + mOldPin = getDefaultOldPin(this, mPhoneAccountHandle); + updateState(State.VerifyOldPin); + } else { + updateState(State.EnterOldPin); + } + } + + private void handleOmtpEvent(OmtpEvents event) { + mConfig.handleEvent(getVoicemailStatusEditor(), event); + } + + private VoicemailStatus.Editor getVoicemailStatusEditor() { + // This activity does not have any automatic retry mechanism, errors should be written right + // away. + return VoicemailStatus.edit(this, mPhoneAccountHandle); + } + + /** + * Extracts the pin length requirement sent by the server with a STATUS SMS. + */ + private void readPinLength() { + VisualVoicemailPreferences preferences = new VisualVoicemailPreferences(this, + mPhoneAccountHandle); + // The OMTP pin length format is {min}-{max} + String[] lengths = preferences.getString(OmtpConstants.TUI_PASSWORD_LENGTH, "").split("-"); + if (lengths.length == 2) { + try { + mPinMinLength = Integer.parseInt(lengths[0]); + mPinMaxLength = Integer.parseInt(lengths[1]); + } catch (NumberFormatException e) { + mPinMinLength = 0; + mPinMaxLength = 0; + } + } else { + mPinMinLength = 0; + mPinMaxLength = 0; + } + } + + @Override + public void onResume() { + super.onResume(); + updateState(mUiState); + + } + + public void handleNext() { + if (mPinEntry.length() == 0) { + return; + } + mUiState.handleNext(this); + } + + public void onClick(View v) { + if (v.getId() == R.id.next_button) { + handleNext(); + } else if (v.getId() == R.id.cancel_button) { + finish(); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + onBackPressed(); + return true; + } + return super.onOptionsItemSelected(item); + } + + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (!mNextButton.isEnabled()) { + return true; + } + // Check if this was the result of hitting the enter or "done" key + if (actionId == EditorInfo.IME_NULL + || actionId == EditorInfo.IME_ACTION_DONE + || actionId == EditorInfo.IME_ACTION_NEXT) { + handleNext(); + return true; + } + return false; + } + + public void afterTextChanged(Editable s) { + mUiState.onInputChanged(this); + } + + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Do nothing + } + + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Do nothing + } + + /** + * After replacing the default PIN with a random PIN, call this to store the random PIN. The + * stored PIN will be automatically entered when the user attempts to change the PIN. + */ + public static void setDefaultOldPIN(Context context, PhoneAccountHandle phoneAccountHandle, + String pin) { + new VisualVoicemailPreferences(context, phoneAccountHandle) + .edit().putString(KEY_DEFAULT_OLD_PIN, pin).apply(); + } + + public static boolean isDefaultOldPinSet(Context context, + PhoneAccountHandle phoneAccountHandle) { + return getDefaultOldPin(context, phoneAccountHandle) != null; + } + + private static String getDefaultOldPin(Context context, PhoneAccountHandle phoneAccountHandle) { + return new VisualVoicemailPreferences(context, phoneAccountHandle) + .getString(KEY_DEFAULT_OLD_PIN); + } + + private String getCurrentPasswordInput() { + return mPinEntry.getText().toString(); + } + + private void updateState(State state) { + State previousState = mUiState; + mUiState = state; + if (previousState != state) { + previousState.onLeave(this); + mPinEntry.setText(""); + mUiState.onEnter(this); + } + mUiState.onInputChanged(this); + } + + /** + * Validates PIN and returns a message to display if PIN fails test. + * + * @param password the raw password the user typed in + * @return error message to show to user or null if password is OK + */ + private CharSequence validatePassword(String password) { + if (mPinMinLength == 0 && mPinMaxLength == 0) { + // Invalid length requirement is sent by the server, just accept anything and let the + // server decide. + return null; + } + + if (password.length() < mPinMinLength) { + return getString(R.string.vm_change_pin_error_too_short); + } + return null; + } + + private void setHeader(int text) { + mHeaderText.setText(text); + mPinEntry.setContentDescription(mHeaderText.getText()); + } + + /** + * Get the corresponding message for the {@link ChangePinResult}.<code>result</code> must not + * {@link OmtpConstants#CHANGE_PIN_SUCCESS} + */ + private CharSequence getChangePinResultMessage(@ChangePinResult int result) { + switch (result) { + case OmtpConstants.CHANGE_PIN_TOO_SHORT: + return getString(R.string.vm_change_pin_error_too_short); + case OmtpConstants.CHANGE_PIN_TOO_LONG: + return getString(R.string.vm_change_pin_error_too_long); + case OmtpConstants.CHANGE_PIN_TOO_WEAK: + return getString(R.string.vm_change_pin_error_too_weak); + case OmtpConstants.CHANGE_PIN_INVALID_CHARACTER: + return getString(R.string.vm_change_pin_error_invalid); + case OmtpConstants.CHANGE_PIN_MISMATCH: + return getString(R.string.vm_change_pin_error_mismatch); + case OmtpConstants.CHANGE_PIN_SYSTEM_ERROR: + return getString(R.string.vm_change_pin_error_system_error); + default: + VvmLog.wtf(TAG, "Unexpected ChangePinResult " + result); + return null; + } + } + + private void verifyOldPin() { + processPinChange(mOldPin, mOldPin); + } + + private void setNextEnabled(boolean enabled) { + mNextButton.setEnabled(enabled); + } + + + private void showError(CharSequence message) { + showError(message, null); + } + + private void showError(CharSequence message, @Nullable OnDismissListener callback) { + new AlertDialog.Builder(this) + .setMessage(message) + .setPositiveButton(android.R.string.ok, null) + .setOnDismissListener(callback) + .show(); + } + + /** + * Asynchronous call to change the PIN on the server. + */ + private void processPinChange(String oldPin, String newPin) { + mProgressDialog = new ProgressDialog(this); + mProgressDialog.setCancelable(false); + mProgressDialog.setMessage(getString(R.string.vm_change_pin_progress_message)); + mProgressDialog.show(); + + ChangePinNetworkRequestCallback callback = new ChangePinNetworkRequestCallback(oldPin, + newPin); + callback.requestNetwork(); + } + + private class ChangePinNetworkRequestCallback extends VvmNetworkRequestCallback { + + private final String mOldPin; + private final String mNewPin; + + public ChangePinNetworkRequestCallback(String oldPin, String newPin) { + super(mConfig, mPhoneAccountHandle, + VoicemailChangePinActivity.this.getVoicemailStatusEditor()); + mOldPin = oldPin; + mNewPin = newPin; + } + + @Override + public void onAvailable(Network network) { + super.onAvailable(network); + try (ImapHelper helper = + new ImapHelper(VoicemailChangePinActivity.this, mPhoneAccountHandle, network, + getVoicemailStatusEditor())) { + + @ChangePinResult int result = + helper.changePin(mOldPin, mNewPin); + sendResult(result); + } catch (InitializingException | MessagingException e) { + VvmLog.e(TAG, "ChangePinNetworkRequestCallback: onAvailable: ", e); + sendResult(OmtpConstants.CHANGE_PIN_SYSTEM_ERROR); + } + } + + @Override + public void onFailed(String reason) { + super.onFailed(reason); + sendResult(OmtpConstants.CHANGE_PIN_SYSTEM_ERROR); + } + + private void sendResult(@ChangePinResult int result) { + VvmLog.i(TAG, "Change PIN result: " + result); + if (mProgressDialog.isShowing() && !VoicemailChangePinActivity.this.isDestroyed() && + !VoicemailChangePinActivity.this.isFinishing()) { + mProgressDialog.dismiss(); + } else { + VvmLog.i(TAG, "Dialog not visible, not dismissing"); + } + mHandler.obtainMessage(MESSAGE_HANDLE_RESULT, result, 0).sendToTarget(); + releaseNetwork(); + } + } + +} |