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