• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.phone.settings;
18 
19 import android.annotation.Nullable;
20 import android.app.Activity;
21 import android.app.AlertDialog;
22 import android.app.ProgressDialog;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.content.DialogInterface.OnDismissListener;
26 import android.content.SharedPreferences;
27 import android.net.Network;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.Message;
31 import android.preference.PreferenceManager;
32 import android.telecom.PhoneAccountHandle;
33 import android.text.Editable;
34 import android.text.InputFilter;
35 import android.text.InputFilter.LengthFilter;
36 import android.text.TextWatcher;
37 import android.view.KeyEvent;
38 import android.view.View;
39 import android.view.View.OnClickListener;
40 import android.view.WindowManager;
41 import android.view.inputmethod.EditorInfo;
42 import android.widget.Button;
43 import android.widget.EditText;
44 import android.widget.TextView;
45 import android.widget.TextView.OnEditorActionListener;
46 import android.widget.Toast;
47 import com.android.phone.PhoneUtils;
48 import com.android.phone.R;
49 import com.android.phone.VoicemailStatus;
50 import com.android.phone.common.mail.MessagingException;
51 import com.android.phone.vvm.omtp.OmtpConstants;
52 import com.android.phone.vvm.omtp.OmtpConstants.ChangePinResult;
53 import com.android.phone.vvm.omtp.OmtpEvents;
54 import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
55 import com.android.phone.vvm.omtp.VisualVoicemailPreferences;
56 import com.android.phone.vvm.omtp.VvmLog;
57 import com.android.phone.vvm.omtp.imap.ImapHelper;
58 import com.android.phone.vvm.omtp.imap.ImapHelper.InitializingException;
59 import com.android.phone.vvm.omtp.sync.VvmNetworkRequestCallback;
60 
61 /**
62  * Dialog to change the voicemail PIN. The TUI (Telephony User Interface) PIN is used when accessing
63  * traditional voicemail through phone call. The intent to launch this activity must contain {@link
64  * #EXTRA_PHONE_ACCOUNT_HANDLE}
65  */
66 public class VoicemailChangePinActivity extends Activity implements OnClickListener,
67         OnEditorActionListener, TextWatcher {
68 
69     private static final String TAG = "VmChangePinActivity";
70 
71     public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "extra_phone_account_handle";
72 
73     private static final String KEY_DEFAULT_OLD_PIN = "default_old_pin";
74 
75     private static final int MESSAGE_HANDLE_RESULT = 1;
76 
77     private PhoneAccountHandle mPhoneAccountHandle;
78     private OmtpVvmCarrierConfigHelper mConfig;
79 
80     private int mPinMinLength;
81     private int mPinMaxLength;
82 
83     private State mUiState = State.Initial;
84     private String mOldPin;
85     private String mFirstPin;
86 
87     private ProgressDialog mProgressDialog;
88 
89     private TextView mHeaderText;
90     private TextView mHintText;
91     private TextView mErrorText;
92     private EditText mPinEntry;
93     private Button mCancelButton;
94     private Button mNextButton;
95 
96     private Handler mHandler = new Handler() {
97         @Override
98         public void handleMessage(Message message) {
99             if (message.what == MESSAGE_HANDLE_RESULT) {
100                 mUiState.handleResult(VoicemailChangePinActivity.this, message.arg1);
101             }
102         }
103     };
104 
105     private enum State {
106         /**
107          * Empty state to handle initial state transition. Will immediately switch into {@link
108          * #VerifyOldPin} if a default PIN has been set by the OMTP client, or {@link #EnterOldPin}
109          * if not.
110          */
111         Initial,
112         /**
113          * Prompt the user to enter old PIN. The PIN will be verified with the server before
114          * proceeding to {@link #EnterNewPin}.
115          */
116         EnterOldPin {
117             @Override
onEnter(VoicemailChangePinActivity activity)118             public void onEnter(VoicemailChangePinActivity activity) {
119                 activity.setHeader(R.string.change_pin_enter_old_pin_header);
120                 activity.mHintText.setText(R.string.change_pin_enter_old_pin_hint);
121                 activity.mNextButton.setText(R.string.change_pin_continue_label);
122                 activity.mErrorText.setText(null);
123             }
124 
125             @Override
onInputChanged(VoicemailChangePinActivity activity)126             public void onInputChanged(VoicemailChangePinActivity activity) {
127                 activity.setNextEnabled(activity.getCurrentPasswordInput().length() > 0);
128             }
129 
130 
131             @Override
handleNext(VoicemailChangePinActivity activity)132             public void handleNext(VoicemailChangePinActivity activity) {
133                 activity.mOldPin = activity.getCurrentPasswordInput();
134                 activity.verifyOldPin();
135             }
136 
137             @Override
handleResult(VoicemailChangePinActivity activity, @ChangePinResult int result)138             public void handleResult(VoicemailChangePinActivity activity,
139                     @ChangePinResult int result) {
140                 if (result == OmtpConstants.CHANGE_PIN_SUCCESS) {
141                     activity.updateState(State.EnterNewPin);
142                 } else {
143                     CharSequence message = activity.getChangePinResultMessage(result);
144                     activity.showError(message);
145                     activity.mPinEntry.setText("");
146                 }
147             }
148         },
149         /**
150          * The default old PIN is found. Show a blank screen while verifying with the server to make
151          * sure the PIN is still valid. If the PIN is still valid, proceed to {@link #EnterNewPin}.
152          * If not, the user probably changed the PIN through other means, proceed to {@link
153          * #EnterOldPin}. If any other issue caused the verifying to fail, show an error and exit.
154          */
155         VerifyOldPin {
156             @Override
onEnter(VoicemailChangePinActivity activity)157             public void onEnter(VoicemailChangePinActivity activity) {
158                 activity.findViewById(android.R.id.content).setVisibility(View.INVISIBLE);
159                 activity.verifyOldPin();
160             }
161 
162             @Override
handleResult(VoicemailChangePinActivity activity, @ChangePinResult int result)163             public void handleResult(VoicemailChangePinActivity activity,
164                     @ChangePinResult int result) {
165                 if (result == OmtpConstants.CHANGE_PIN_SUCCESS) {
166                     activity.updateState(State.EnterNewPin);
167                 } else if (result == OmtpConstants.CHANGE_PIN_SYSTEM_ERROR) {
168                     activity.getWindow().setSoftInputMode(
169                             WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
170                     activity.showError(activity.getString(R.string.change_pin_system_error),
171                             new OnDismissListener() {
172                                 @Override
173                                 public void onDismiss(DialogInterface dialog) {
174                                     activity.finish();
175                                 }
176                             });
177                 } else {
178                     VvmLog.e(TAG, "invalid default old PIN: " + activity
179                             .getChangePinResultMessage(result));
180                     // If the default old PIN is rejected by the server, the PIN is probably changed
181                     // through other means, or the generated pin is invalid
182                     // Wipe the default old PIN so the old PIN input box will be shown to the user
183                     // on the next time.
184                     setDefaultOldPIN(activity, activity.mPhoneAccountHandle, null);
185                     activity.handleOmtpEvent(OmtpEvents.CONFIG_PIN_SET);
186                     activity.updateState(State.EnterOldPin);
187                 }
188             }
189 
190             @Override
onLeave(VoicemailChangePinActivity activity)191             public void onLeave(VoicemailChangePinActivity activity) {
192                 activity.findViewById(android.R.id.content).setVisibility(View.VISIBLE);
193             }
194         },
195         /**
196          * Let the user enter the new PIN and validate the format. Only length is enforced, PIN
197          * strength check relies on the server. After a valid PIN is entered, proceed to {@link
198          * #ConfirmNewPin}
199          */
200         EnterNewPin {
201             @Override
onEnter(VoicemailChangePinActivity activity)202             public void onEnter(VoicemailChangePinActivity activity) {
203                 activity.mHeaderText.setText(R.string.change_pin_enter_new_pin_header);
204                 activity.mNextButton.setText(R.string.change_pin_continue_label);
205                 activity.mHintText.setText(
206                         activity.getString(R.string.change_pin_enter_new_pin_hint,
207                                 activity.mPinMinLength, activity.mPinMaxLength));
208             }
209 
210             @Override
onInputChanged(VoicemailChangePinActivity activity)211             public void onInputChanged(VoicemailChangePinActivity activity) {
212                 String password = activity.getCurrentPasswordInput();
213                 if (password.length() == 0) {
214                     activity.setNextEnabled(false);
215                     return;
216                 }
217                 CharSequence error = activity.validatePassword(password);
218                 if (error != null) {
219                     activity.mErrorText.setText(error);
220                     activity.setNextEnabled(false);
221                 } else {
222                     activity.mErrorText.setText(null);
223                     activity.setNextEnabled(true);
224                 }
225             }
226 
227             @Override
handleNext(VoicemailChangePinActivity activity)228             public void handleNext(VoicemailChangePinActivity activity) {
229                 CharSequence errorMsg;
230                 errorMsg = activity.validatePassword(activity.getCurrentPasswordInput());
231                 if (errorMsg != null) {
232                     activity.showError(errorMsg);
233                     return;
234                 }
235                 activity.mFirstPin = activity.getCurrentPasswordInput();
236                 activity.updateState(State.ConfirmNewPin);
237             }
238         },
239         /**
240          * Let the user type in the same PIN again to avoid typos. If the PIN matches then perform a
241          * PIN change to the server. Finish the activity if succeeded. Return to {@link
242          * #EnterOldPin} if the old PIN is rejected, {@link #EnterNewPin} for other failure.
243          */
244         ConfirmNewPin {
245             @Override
onEnter(VoicemailChangePinActivity activity)246             public void onEnter(VoicemailChangePinActivity activity) {
247                 activity.mHeaderText.setText(R.string.change_pin_confirm_pin_header);
248                 activity.mHintText.setText(null);
249                 activity.mNextButton.setText(R.string.change_pin_ok_label);
250             }
251 
252             @Override
onInputChanged(VoicemailChangePinActivity activity)253             public void onInputChanged(VoicemailChangePinActivity activity) {
254                 if (activity.getCurrentPasswordInput().length() == 0) {
255                     activity.setNextEnabled(false);
256                     return;
257                 }
258                 if (activity.getCurrentPasswordInput().equals(activity.mFirstPin)) {
259                     activity.setNextEnabled(true);
260                     activity.mErrorText.setText(null);
261                 } else {
262                     activity.setNextEnabled(false);
263                     activity.mErrorText.setText(R.string.change_pin_confirm_pins_dont_match);
264                 }
265             }
266 
267             @Override
handleResult(VoicemailChangePinActivity activity, @ChangePinResult int result)268             public void handleResult(VoicemailChangePinActivity activity,
269                     @ChangePinResult int result) {
270                 if (result == OmtpConstants.CHANGE_PIN_SUCCESS) {
271                     // If the PIN change succeeded we no longer know what the old (current) PIN is.
272                     // Wipe the default old PIN so the old PIN input box will be shown to the user
273                     // on the next time.
274                     setDefaultOldPIN(activity, activity.mPhoneAccountHandle, null);
275                     activity.handleOmtpEvent(OmtpEvents.CONFIG_PIN_SET);
276 
277                     activity.finish();
278 
279                     Toast.makeText(activity, activity.getString(R.string.change_pin_succeeded),
280                             Toast.LENGTH_SHORT).show();
281                 } else {
282                     CharSequence message = activity.getChangePinResultMessage(result);
283                     VvmLog.i(TAG, "Change PIN failed: " + message);
284                     activity.showError(message);
285                     if (result == OmtpConstants.CHANGE_PIN_MISMATCH) {
286                         // Somehow the PIN has changed, prompt to enter the old PIN again.
287                         activity.updateState(State.EnterOldPin);
288                     } else {
289                         // The new PIN failed to fulfil other restrictions imposed by the server.
290                         activity.updateState(State.EnterNewPin);
291                     }
292 
293                 }
294 
295             }
296 
297             @Override
handleNext(VoicemailChangePinActivity activity)298             public void handleNext(VoicemailChangePinActivity activity) {
299                 activity.processPinChange(activity.mOldPin, activity.mFirstPin);
300             }
301         };
302 
303         /**
304          * The activity has switched from another state to this one.
305          */
onEnter(VoicemailChangePinActivity activity)306         public void onEnter(VoicemailChangePinActivity activity) {
307             // Do nothing
308         }
309 
310         /**
311          * The user has typed something into the PIN input field. Also called after {@link
312          * #onEnter(VoicemailChangePinActivity)}
313          */
onInputChanged(VoicemailChangePinActivity activity)314         public void onInputChanged(VoicemailChangePinActivity activity) {
315             // Do nothing
316         }
317 
318         /**
319          * The asynchronous call to change the PIN on the server has returned.
320          */
handleResult(VoicemailChangePinActivity activity, @ChangePinResult int result)321         public void handleResult(VoicemailChangePinActivity activity, @ChangePinResult int result) {
322             // Do nothing
323         }
324 
325         /**
326          * The user has pressed the "next" button.
327          */
handleNext(VoicemailChangePinActivity activity)328         public void handleNext(VoicemailChangePinActivity activity) {
329             // Do nothing
330         }
331 
332         /**
333          * The activity has switched from this state to another one.
334          */
onLeave(VoicemailChangePinActivity activity)335         public void onLeave(VoicemailChangePinActivity activity) {
336             // Do nothing
337         }
338 
339     }
340 
341     @Override
onCreate(Bundle savedInstanceState)342     public void onCreate(Bundle savedInstanceState) {
343         super.onCreate(savedInstanceState);
344 
345         mPhoneAccountHandle = getIntent().getParcelableExtra(EXTRA_PHONE_ACCOUNT_HANDLE);
346         mConfig = new OmtpVvmCarrierConfigHelper(this, mPhoneAccountHandle);
347         setContentView(R.layout.voicemail_change_pin);
348         setTitle(R.string.change_pin_title);
349 
350         readPinLength();
351 
352         View view = findViewById(android.R.id.content);
353 
354         mCancelButton = (Button) view.findViewById(R.id.cancel_button);
355         mCancelButton.setOnClickListener(this);
356         mNextButton = (Button) view.findViewById(R.id.next_button);
357         mNextButton.setOnClickListener(this);
358 
359         mPinEntry = (EditText) view.findViewById(R.id.pin_entry);
360         mPinEntry.setOnEditorActionListener(this);
361         mPinEntry.addTextChangedListener(this);
362         if (mPinMaxLength != 0) {
363             mPinEntry.setFilters(new InputFilter[]{new LengthFilter(mPinMaxLength)});
364         }
365 
366 
367         mHeaderText = (TextView) view.findViewById(R.id.headerText);
368         mHintText = (TextView) view.findViewById(R.id.hintText);
369         mErrorText = (TextView) view.findViewById(R.id.errorText);
370 
371         migrateDefaultOldPin();
372 
373         if (isDefaultOldPinSet(this, mPhoneAccountHandle)) {
374             mOldPin = getDefaultOldPin(this, mPhoneAccountHandle);
375             updateState(State.VerifyOldPin);
376         } else {
377             updateState(State.EnterOldPin);
378         }
379     }
380 
handleOmtpEvent(OmtpEvents event)381     private void handleOmtpEvent(OmtpEvents event) {
382         mConfig.handleEvent(getVoicemailStatusEditor(), event);
383     }
384 
getVoicemailStatusEditor()385     private VoicemailStatus.Editor getVoicemailStatusEditor() {
386         // This activity does not have any automatic retry mechanism, errors should be written right
387         // away.
388         return VoicemailStatus.edit(this, mPhoneAccountHandle);
389     }
390 
391     /**
392      * Extracts the pin length requirement sent by the server with a STATUS SMS.
393      */
readPinLength()394     private void readPinLength() {
395         VisualVoicemailPreferences preferences = new VisualVoicemailPreferences(this,
396                 mPhoneAccountHandle);
397         // The OMTP pin length format is {min}-{max}
398         String[] lengths = preferences.getString(OmtpConstants.TUI_PASSWORD_LENGTH, "").split("-");
399         if (lengths.length == 2) {
400             try {
401                 mPinMinLength = Integer.parseInt(lengths[0]);
402                 mPinMaxLength = Integer.parseInt(lengths[1]);
403             } catch (NumberFormatException e) {
404                 mPinMinLength = 0;
405                 mPinMaxLength = 0;
406             }
407         } else {
408             mPinMinLength = 0;
409             mPinMaxLength = 0;
410         }
411     }
412 
413     @Override
onResume()414     public void onResume() {
415         super.onResume();
416         updateState(mUiState);
417 
418     }
419 
handleNext()420     public void handleNext() {
421         if (mPinEntry.length() == 0) {
422             return;
423         }
424         mUiState.handleNext(this);
425     }
426 
onClick(View v)427     public void onClick(View v) {
428         switch (v.getId()) {
429             case R.id.next_button:
430                 handleNext();
431                 break;
432 
433             case R.id.cancel_button:
434                 finish();
435                 break;
436         }
437     }
438 
onEditorAction(TextView v, int actionId, KeyEvent event)439     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
440         // Check if this was the result of hitting the enter or "done" key
441         if (actionId == EditorInfo.IME_NULL
442                 || actionId == EditorInfo.IME_ACTION_DONE
443                 || actionId == EditorInfo.IME_ACTION_NEXT) {
444             handleNext();
445             return true;
446         }
447         return false;
448     }
449 
afterTextChanged(Editable s)450     public void afterTextChanged(Editable s) {
451         mUiState.onInputChanged(this);
452     }
453 
beforeTextChanged(CharSequence s, int start, int count, int after)454     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
455         // Do nothing
456     }
457 
onTextChanged(CharSequence s, int start, int before, int count)458     public void onTextChanged(CharSequence s, int start, int before, int count) {
459         // Do nothing
460     }
461 
462     /**
463      * After replacing the default PIN with a random PIN, call this to store the random PIN. The
464      * stored PIN will be automatically entered when the user attempts to change the PIN.
465      */
setDefaultOldPIN(Context context, PhoneAccountHandle phoneAccountHandle, String pin)466     public static void setDefaultOldPIN(Context context, PhoneAccountHandle phoneAccountHandle,
467             String pin) {
468         new VisualVoicemailPreferences(context, phoneAccountHandle)
469                 .edit().putString(KEY_DEFAULT_OLD_PIN, pin).apply();
470     }
471 
isDefaultOldPinSet(Context context, PhoneAccountHandle phoneAccountHandle)472     public static boolean isDefaultOldPinSet(Context context,
473             PhoneAccountHandle phoneAccountHandle) {
474         return getDefaultOldPin(context, phoneAccountHandle) != null;
475     }
476 
getDefaultOldPin(Context context, PhoneAccountHandle phoneAccountHandle)477     private static String getDefaultOldPin(Context context, PhoneAccountHandle phoneAccountHandle) {
478         return new VisualVoicemailPreferences(context, phoneAccountHandle)
479                 .getString(KEY_DEFAULT_OLD_PIN);
480     }
481 
482     /**
483      * Storage location has changed mid development. Migrate from the old location to avoid losing
484      * tester's default old pin.
485      */
migrateDefaultOldPin()486     private void migrateDefaultOldPin() {
487         String key = "voicemail_pin_dialog_preference_"
488                 + PhoneUtils.getSubIdForPhoneAccountHandle(mPhoneAccountHandle)
489                 + "_default_old_pin";
490 
491         SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
492         if (preferences.contains(key)) {
493             setDefaultOldPIN(this, mPhoneAccountHandle, preferences.getString(key, null));
494             preferences.edit().putString(key, null).apply();
495         }
496     }
497 
getCurrentPasswordInput()498     private String getCurrentPasswordInput() {
499         return mPinEntry.getText().toString();
500     }
501 
updateState(State state)502     private void updateState(State state) {
503         State previousState = mUiState;
504         mUiState = state;
505         if (previousState != state) {
506             previousState.onLeave(this);
507             mPinEntry.setText("");
508             mUiState.onEnter(this);
509         }
510         mUiState.onInputChanged(this);
511     }
512 
513     /**
514      * Validates PIN and returns a message to display if PIN fails test.
515      *
516      * @param password the raw password the user typed in
517      * @return error message to show to user or null if password is OK
518      */
validatePassword(String password)519     private CharSequence validatePassword(String password) {
520         if (mPinMinLength == 0 && mPinMaxLength == 0) {
521             // Invalid length requirement is sent by the server, just accept anything and let the
522             // server decide.
523             return null;
524         }
525 
526         if (password.length() < mPinMinLength) {
527             return getString(R.string.vm_change_pin_error_too_short);
528         }
529         return null;
530     }
531 
setHeader(int text)532     private void setHeader(int text) {
533         mHeaderText.setText(text);
534         mPinEntry.setContentDescription(mHeaderText.getText());
535     }
536 
537     /**
538      * Get the corresponding message for the {@link ChangePinResult}.<code>result</code> must not
539      * {@link OmtpConstants#CHANGE_PIN_SUCCESS}
540      */
getChangePinResultMessage(@hangePinResult int result)541     private CharSequence getChangePinResultMessage(@ChangePinResult int result) {
542         switch (result) {
543             case OmtpConstants.CHANGE_PIN_TOO_SHORT:
544                 return getString(R.string.vm_change_pin_error_too_short);
545             case OmtpConstants.CHANGE_PIN_TOO_LONG:
546                 return getString(R.string.vm_change_pin_error_too_long);
547             case OmtpConstants.CHANGE_PIN_TOO_WEAK:
548                 return getString(R.string.vm_change_pin_error_too_weak);
549             case OmtpConstants.CHANGE_PIN_INVALID_CHARACTER:
550                 return getString(R.string.vm_change_pin_error_invalid);
551             case OmtpConstants.CHANGE_PIN_MISMATCH:
552                 return getString(R.string.vm_change_pin_error_mismatch);
553             case OmtpConstants.CHANGE_PIN_SYSTEM_ERROR:
554                 return getString(R.string.vm_change_pin_error_system_error);
555             default:
556                 VvmLog.wtf(TAG, "Unexpected ChangePinResult " + result);
557                 return null;
558         }
559     }
560 
verifyOldPin()561     private void verifyOldPin() {
562         processPinChange(mOldPin, mOldPin);
563     }
564 
setNextEnabled(boolean enabled)565     private void setNextEnabled(boolean enabled) {
566         mNextButton.setEnabled(enabled);
567     }
568 
569 
showError(CharSequence message)570     private void showError(CharSequence message) {
571         showError(message, null);
572     }
573 
showError(CharSequence message, @Nullable OnDismissListener callback)574     private void showError(CharSequence message, @Nullable OnDismissListener callback) {
575         new AlertDialog.Builder(this)
576                 .setMessage(message)
577                 .setPositiveButton(android.R.string.ok, null)
578                 .setOnDismissListener(callback)
579                 .show();
580     }
581 
582     /**
583      * Asynchronous call to change the PIN on the server.
584      */
processPinChange(String oldPin, String newPin)585     private void processPinChange(String oldPin, String newPin) {
586         mProgressDialog = new ProgressDialog(this);
587         mProgressDialog.setCancelable(false);
588         mProgressDialog.setMessage(getString(R.string.vm_change_pin_progress_message));
589         mProgressDialog.show();
590 
591         ChangePinNetworkRequestCallback callback = new ChangePinNetworkRequestCallback(oldPin,
592                 newPin);
593         callback.requestNetwork();
594     }
595 
596     private class ChangePinNetworkRequestCallback extends VvmNetworkRequestCallback {
597 
598         private final String mOldPin;
599         private final String mNewPin;
600 
ChangePinNetworkRequestCallback(String oldPin, String newPin)601         public ChangePinNetworkRequestCallback(String oldPin, String newPin) {
602             super(mConfig, mPhoneAccountHandle,
603                 VoicemailChangePinActivity.this.getVoicemailStatusEditor());
604             mOldPin = oldPin;
605             mNewPin = newPin;
606         }
607 
608         @Override
onAvailable(Network network)609         public void onAvailable(Network network) {
610             super.onAvailable(network);
611             try (ImapHelper helper =
612                 new ImapHelper(VoicemailChangePinActivity.this, mPhoneAccountHandle, network,
613                     getVoicemailStatusEditor())) {
614 
615                 @ChangePinResult int result =
616                         helper.changePin(mOldPin, mNewPin);
617                 sendResult(result);
618             } catch (InitializingException | MessagingException e) {
619                 VvmLog.e(TAG, "ChangePinNetworkRequestCallback: onAvailable: ", e);
620                 sendResult(OmtpConstants.CHANGE_PIN_SYSTEM_ERROR);
621             }
622         }
623 
624         @Override
onFailed(String reason)625         public void onFailed(String reason) {
626             super.onFailed(reason);
627             sendResult(OmtpConstants.CHANGE_PIN_SYSTEM_ERROR);
628         }
629 
sendResult(@hangePinResult int result)630         private void sendResult(@ChangePinResult int result) {
631             VvmLog.i(TAG, "Change PIN result: " + result);
632             if (mProgressDialog.isShowing() && !VoicemailChangePinActivity.this.isDestroyed() &&
633                     !VoicemailChangePinActivity.this.isFinishing()) {
634                 mProgressDialog.dismiss();
635             } else {
636                 VvmLog.i(TAG, "Dialog not visible, not dismissing");
637             }
638             mHandler.obtainMessage(MESSAGE_HANDLE_RESULT, result, 0).sendToTarget();
639             releaseNetwork();
640         }
641     }
642 
643 }
644