fe523e158782685c438cd0620762979e572af0c9
[strongswan.git] / src / frontends / android / app / src / main / java / org / strongswan / android / ui / VpnProfileDetailActivity.java
1 /*
2 * Copyright (C) 2012-2016 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * HSR Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 package org.strongswan.android.ui;
19
20 import android.app.Dialog;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.content.Intent;
24 import android.os.AsyncTask;
25 import android.os.Bundle;
26 import android.security.KeyChain;
27 import android.security.KeyChainAliasCallback;
28 import android.security.KeyChainException;
29 import android.support.v7.app.AlertDialog;
30 import android.support.v7.app.AppCompatActivity;
31 import android.support.v7.app.AppCompatDialogFragment;
32 import android.text.Editable;
33 import android.text.Html;
34 import android.text.TextUtils;
35 import android.text.TextWatcher;
36 import android.util.Log;
37 import android.view.Menu;
38 import android.view.MenuInflater;
39 import android.view.MenuItem;
40 import android.view.View;
41 import android.view.View.OnClickListener;
42 import android.view.ViewGroup;
43 import android.widget.AdapterView;
44 import android.widget.AdapterView.OnItemSelectedListener;
45 import android.widget.CheckBox;
46 import android.widget.CompoundButton;
47 import android.widget.CompoundButton.OnCheckedChangeListener;
48 import android.widget.EditText;
49 import android.widget.RelativeLayout;
50 import android.widget.Spinner;
51 import android.widget.TextView;
52
53 import org.strongswan.android.R;
54 import org.strongswan.android.data.VpnProfile;
55 import org.strongswan.android.data.VpnProfileDataSource;
56 import org.strongswan.android.data.VpnType;
57 import org.strongswan.android.data.VpnType.VpnTypeFeature;
58 import org.strongswan.android.logic.TrustedCertificateManager;
59 import org.strongswan.android.security.TrustedCertificateEntry;
60 import org.strongswan.android.ui.widget.TextInputLayoutHelper;
61
62 import java.security.cert.X509Certificate;
63
64 public class VpnProfileDetailActivity extends AppCompatActivity
65 {
66 private static final int SELECT_TRUSTED_CERTIFICATE = 0;
67 private static final int MTU_MIN = 1280;
68 private static final int MTU_MAX = 1500;
69
70 private VpnProfileDataSource mDataSource;
71 private Long mId;
72 private TrustedCertificateEntry mCertEntry;
73 private String mUserCertLoading;
74 private TrustedCertificateEntry mUserCertEntry;
75 private VpnType mVpnType = VpnType.IKEV2_EAP;
76 private VpnProfile mProfile;
77 private EditText mName;
78 private TextInputLayoutHelper mNameWrap;
79 private EditText mGateway;
80 private TextInputLayoutHelper mGatewayWrap;
81 private Spinner mSelectVpnType;
82 private ViewGroup mUsernamePassword;
83 private EditText mUsername;
84 private TextInputLayoutHelper mUsernameWrap;
85 private EditText mPassword;
86 private ViewGroup mUserCertificate;
87 private RelativeLayout mSelectUserCert;
88 private CheckBox mCheckAuto;
89 private RelativeLayout mSelectCert;
90 private RelativeLayout mTncNotice;
91 private CheckBox mShowAdvanced;
92 private ViewGroup mAdvancedSettings;
93 private EditText mMTU;
94 private TextInputLayoutHelper mMTUWrap;
95 private EditText mPort;
96 private TextInputLayoutHelper mPortWrap;
97 private CheckBox mBlockIPv4;
98 private CheckBox mBlockIPv6;
99
100 @Override
101 public void onCreate(Bundle savedInstanceState)
102 {
103 super.onCreate(savedInstanceState);
104
105 /* the title is set when we load the profile, if any */
106 getSupportActionBar().setDisplayHomeAsUpEnabled(true);
107
108 mDataSource = new VpnProfileDataSource(this);
109 mDataSource.open();
110
111 setContentView(R.layout.profile_detail_view);
112
113 mName = (EditText)findViewById(R.id.name);
114 mNameWrap = (TextInputLayoutHelper)findViewById(R.id.name_wrap);
115 mGateway = (EditText)findViewById(R.id.gateway);
116 mGatewayWrap = (TextInputLayoutHelper) findViewById(R.id.gateway_wrap);
117 mSelectVpnType = (Spinner)findViewById(R.id.vpn_type);
118 mTncNotice = (RelativeLayout)findViewById(R.id.tnc_notice);
119
120 mUsernamePassword = (ViewGroup)findViewById(R.id.username_password_group);
121 mUsername = (EditText)findViewById(R.id.username);
122 mUsernameWrap = (TextInputLayoutHelper) findViewById(R.id.username_wrap);
123 mPassword = (EditText)findViewById(R.id.password);
124
125 mUserCertificate = (ViewGroup)findViewById(R.id.user_certificate_group);
126 mSelectUserCert = (RelativeLayout)findViewById(R.id.select_user_certificate);
127
128 mCheckAuto = (CheckBox)findViewById(R.id.ca_auto);
129 mSelectCert = (RelativeLayout)findViewById(R.id.select_certificate);
130
131 mShowAdvanced = (CheckBox)findViewById(R.id.show_advanced);
132 mAdvancedSettings = (ViewGroup)findViewById(R.id.advanced_settings);
133
134 mMTU = (EditText)findViewById(R.id.mtu);
135 mMTUWrap = (TextInputLayoutHelper) findViewById(R.id.mtu_wrap);
136 mPort = (EditText)findViewById(R.id.port);
137 mPortWrap = (TextInputLayoutHelper) findViewById(R.id.port_wrap);
138 mBlockIPv4 = (CheckBox)findViewById(R.id.split_tunneling_v4);
139 mBlockIPv6 = (CheckBox)findViewById(R.id.split_tunneling_v6);
140
141 mGateway.addTextChangedListener(new TextWatcher() {
142 @Override
143 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
144
145 @Override
146 public void onTextChanged(CharSequence s, int start, int before, int count) {}
147
148 @Override
149 public void afterTextChanged(Editable s)
150 {
151 if (TextUtils.isEmpty(mGateway.getText()))
152 {
153 mNameWrap.setHelperText(getString(R.string.profile_name_hint));
154 }
155 else
156 {
157 mNameWrap.setHelperText(String.format(getString(R.string.profile_name_hint_gateway), mGateway.getText()));
158 }
159 }
160 });
161
162 mSelectVpnType.setOnItemSelectedListener(new OnItemSelectedListener() {
163 @Override
164 public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
165 {
166 mVpnType = VpnType.values()[position];
167 updateCredentialView();
168 }
169
170 @Override
171 public void onNothingSelected(AdapterView<?> parent)
172 { /* should not happen */
173 mVpnType = VpnType.IKEV2_EAP;
174 updateCredentialView();
175 }
176 });
177
178 ((TextView)mTncNotice.findViewById(android.R.id.text1)).setText(R.string.tnc_notice_title);
179 ((TextView)mTncNotice.findViewById(android.R.id.text2)).setText(R.string.tnc_notice_subtitle);
180 mTncNotice.setOnClickListener(new OnClickListener() {
181 @Override
182 public void onClick(View v)
183 {
184 new TncNoticeDialog().show(VpnProfileDetailActivity.this.getSupportFragmentManager(), "TncNotice");
185 }
186 });
187
188 mSelectUserCert.setOnClickListener(new SelectUserCertOnClickListener());
189
190 mCheckAuto.setOnCheckedChangeListener(new OnCheckedChangeListener() {
191 @Override
192 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
193 {
194 updateCertificateSelector();
195 }
196 });
197
198 mSelectCert.setOnClickListener(new OnClickListener() {
199 @Override
200 public void onClick(View v)
201 {
202 Intent intent = new Intent(VpnProfileDetailActivity.this, TrustedCertificatesActivity.class);
203 intent.setAction(TrustedCertificatesActivity.SELECT_CERTIFICATE);
204 startActivityForResult(intent, SELECT_TRUSTED_CERTIFICATE);
205 }
206 });
207
208 mShowAdvanced.setOnCheckedChangeListener(new OnCheckedChangeListener() {
209 @Override
210 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
211 {
212 updateAdvancedSettings();
213 }
214 });
215
216 mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID);
217 if (mId == null)
218 {
219 Bundle extras = getIntent().getExtras();
220 mId = extras == null ? null : extras.getLong(VpnProfileDataSource.KEY_ID);
221 }
222
223 loadProfileData(savedInstanceState);
224
225 updateCredentialView();
226 updateCertificateSelector();
227 updateAdvancedSettings();
228 }
229
230 @Override
231 protected void onDestroy()
232 {
233 super.onDestroy();
234 mDataSource.close();
235 }
236
237 @Override
238 protected void onSaveInstanceState(Bundle outState)
239 {
240 super.onSaveInstanceState(outState);
241 if (mId != null)
242 {
243 outState.putLong(VpnProfileDataSource.KEY_ID, mId);
244 }
245 if (mUserCertEntry != null)
246 {
247 outState.putString(VpnProfileDataSource.KEY_USER_CERTIFICATE, mUserCertEntry.getAlias());
248 }
249 if (mCertEntry != null)
250 {
251 outState.putString(VpnProfileDataSource.KEY_CERTIFICATE, mCertEntry.getAlias());
252 }
253 }
254
255 @Override
256 public boolean onCreateOptionsMenu(Menu menu)
257 {
258 MenuInflater inflater = getMenuInflater();
259 inflater.inflate(R.menu.profile_edit, menu);
260 return true;
261 }
262
263 @Override
264 public boolean onOptionsItemSelected(MenuItem item)
265 {
266 switch (item.getItemId())
267 {
268 case android.R.id.home:
269 case R.id.menu_cancel:
270 finish();
271 return true;
272 case R.id.menu_accept:
273 saveProfile();
274 return true;
275 default:
276 return super.onOptionsItemSelected(item);
277 }
278 }
279
280 @Override
281 protected void onActivityResult(int requestCode, int resultCode, Intent data)
282 {
283 switch (requestCode)
284 {
285 case SELECT_TRUSTED_CERTIFICATE:
286 if (resultCode == RESULT_OK)
287 {
288 String alias = data.getStringExtra(VpnProfileDataSource.KEY_CERTIFICATE);
289 X509Certificate certificate = TrustedCertificateManager.getInstance().getCACertificateFromAlias(alias);
290 mCertEntry = certificate == null ? null : new TrustedCertificateEntry(alias, certificate);
291 updateCertificateSelector();
292 }
293 break;
294 default:
295 super.onActivityResult(requestCode, resultCode, data);
296 }
297 }
298
299 /**
300 * Update the UI to enter credentials depending on the type of VPN currently selected
301 */
302 private void updateCredentialView()
303 {
304 mUsernamePassword.setVisibility(mVpnType.has(VpnTypeFeature.USER_PASS) ? View.VISIBLE : View.GONE);
305 mUserCertificate.setVisibility(mVpnType.has(VpnTypeFeature.CERTIFICATE) ? View.VISIBLE : View.GONE);
306 mTncNotice.setVisibility(mVpnType.has(VpnTypeFeature.BYOD) ? View.VISIBLE : View.GONE);
307
308 if (mVpnType.has(VpnTypeFeature.CERTIFICATE))
309 {
310 if (mUserCertLoading != null)
311 {
312 ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setText(mUserCertLoading);
313 ((TextView)mSelectUserCert.findViewById(android.R.id.text2)).setText(R.string.loading);
314 }
315 else if (mUserCertEntry != null)
316 { /* clear any errors and set the new data */
317 ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setError(null);
318 ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setText(mUserCertEntry.getAlias());
319 ((TextView)mSelectUserCert.findViewById(android.R.id.text2)).setText(mUserCertEntry.getCertificate().getSubjectDN().toString());
320 }
321 else
322 {
323 ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setText(R.string.profile_user_select_certificate_label);
324 ((TextView)mSelectUserCert.findViewById(android.R.id.text2)).setText(R.string.profile_user_select_certificate);
325 }
326 }
327 }
328
329 /**
330 * Show an alert in case the previously selected certificate is not found anymore
331 * or the user did not select a certificate in the spinner.
332 */
333 private void showCertificateAlert()
334 {
335 AlertDialog.Builder adb = new AlertDialog.Builder(VpnProfileDetailActivity.this);
336 adb.setTitle(R.string.alert_text_nocertfound_title);
337 adb.setMessage(R.string.alert_text_nocertfound);
338 adb.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
339 @Override
340 public void onClick(DialogInterface dialog, int id)
341 {
342 dialog.cancel();
343 }
344 });
345 adb.show();
346 }
347
348 /**
349 * Update the CA certificate selection UI depending on whether the
350 * certificate should be automatically selected or not.
351 */
352 private void updateCertificateSelector()
353 {
354 if (!mCheckAuto.isChecked())
355 {
356 mSelectCert.setEnabled(true);
357 mSelectCert.setVisibility(View.VISIBLE);
358
359 if (mCertEntry != null)
360 {
361 ((TextView)mSelectCert.findViewById(android.R.id.text1)).setText(mCertEntry.getSubjectPrimary());
362 ((TextView)mSelectCert.findViewById(android.R.id.text2)).setText(mCertEntry.getSubjectSecondary());
363 }
364 else
365 {
366 ((TextView)mSelectCert.findViewById(android.R.id.text1)).setText(R.string.profile_ca_select_certificate_label);
367 ((TextView)mSelectCert.findViewById(android.R.id.text2)).setText(R.string.profile_ca_select_certificate);
368 }
369 }
370 else
371 {
372 mSelectCert.setEnabled(false);
373 mSelectCert.setVisibility(View.GONE);
374 }
375 }
376
377 /**
378 * Update the advanced settings UI depending on whether any advanced
379 * settings have already been made.
380 */
381 private void updateAdvancedSettings()
382 {
383 boolean show = mShowAdvanced.isChecked();
384 if (!show && mProfile != null)
385 {
386 Integer st = mProfile.getSplitTunneling();
387 show = mProfile.getMTU() != null || mProfile.getPort() != null || (st != null && st != 0);
388 }
389 mShowAdvanced.setVisibility(!show ? View.VISIBLE : View.GONE);
390 mAdvancedSettings.setVisibility(show ? View.VISIBLE : View.GONE);
391 }
392
393 /**
394 * Save or update the profile depending on whether we actually have a
395 * profile object or not (this was created in updateProfileData)
396 */
397 private void saveProfile()
398 {
399 if (verifyInput())
400 {
401 if (mProfile != null)
402 {
403 updateProfileData();
404 mDataSource.updateVpnProfile(mProfile);
405 }
406 else
407 {
408 mProfile = new VpnProfile();
409 updateProfileData();
410 mDataSource.insertProfile(mProfile);
411 }
412 setResult(RESULT_OK, new Intent().putExtra(VpnProfileDataSource.KEY_ID, mProfile.getId()));
413 finish();
414 }
415 }
416
417 /**
418 * Verify the user input and display error messages.
419 * @return true if the input is valid
420 */
421 private boolean verifyInput()
422 {
423 boolean valid = true;
424 if (mGateway.getText().toString().trim().isEmpty())
425 {
426 mGatewayWrap.setError(getString(R.string.alert_text_no_input_gateway));
427 valid = false;
428 }
429 if (mVpnType.has(VpnTypeFeature.USER_PASS))
430 {
431 if (mUsername.getText().toString().trim().isEmpty())
432 {
433 mUsernameWrap.setError(getString(R.string.alert_text_no_input_username));
434 valid = false;
435 }
436 }
437 if (mVpnType.has(VpnTypeFeature.CERTIFICATE) && mUserCertEntry == null)
438 { /* let's show an error icon */
439 ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setError("");
440 valid = false;
441 }
442 if (!mCheckAuto.isChecked() && mCertEntry == null)
443 {
444 showCertificateAlert();
445 valid = false;
446 }
447 Integer mtu = getInteger(mMTU);
448 if (mtu != null && (mtu < MTU_MIN || mtu > MTU_MAX))
449 {
450 mMTUWrap.setError(String.format(getString(R.string.alert_text_out_of_range), MTU_MIN, MTU_MAX));
451 valid = false;
452 }
453 Integer port = getInteger(mPort);
454 if (port != null && (port < 1 || port > 65535))
455 {
456 mPortWrap.setError(String.format(getString(R.string.alert_text_out_of_range), 1, 65535));
457 valid = false;
458 }
459 return valid;
460 }
461
462 /**
463 * Update the profile object with the data entered by the user
464 */
465 private void updateProfileData()
466 {
467 /* the name is optional, we default to the gateway if none is given */
468 String name = mName.getText().toString().trim();
469 String gateway = mGateway.getText().toString().trim();
470 mProfile.setName(name.isEmpty() ? gateway : name);
471 mProfile.setGateway(gateway);
472 mProfile.setVpnType(mVpnType);
473 if (mVpnType.has(VpnTypeFeature.USER_PASS))
474 {
475 mProfile.setUsername(mUsername.getText().toString().trim());
476 String password = mPassword.getText().toString().trim();
477 password = password.isEmpty() ? null : password;
478 mProfile.setPassword(password);
479 }
480 if (mVpnType.has(VpnTypeFeature.CERTIFICATE))
481 {
482 mProfile.setUserCertificateAlias(mUserCertEntry.getAlias());
483 }
484 String certAlias = mCheckAuto.isChecked() ? null : mCertEntry.getAlias();
485 mProfile.setCertificateAlias(certAlias);
486 mProfile.setMTU(getInteger(mMTU));
487 mProfile.setPort(getInteger(mPort));
488 int st = 0;
489 st |= mBlockIPv4.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4 : 0;
490 st |= mBlockIPv6.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6 : 0;
491 mProfile.setSplitTunneling(st == 0 ? null : st);
492 }
493
494 /**
495 * Load an existing profile if we got an ID
496 *
497 * @param savedInstanceState previously saved state
498 */
499 private void loadProfileData(Bundle savedInstanceState)
500 {
501 String useralias = null, alias = null;
502
503 getSupportActionBar().setTitle(R.string.add_profile);
504 if (mId != null && mId != 0)
505 {
506 mProfile = mDataSource.getVpnProfile(mId);
507 if (mProfile != null)
508 {
509 mName.setText(mProfile.getName());
510 mGateway.setText(mProfile.getGateway());
511 mVpnType = mProfile.getVpnType();
512 mUsername.setText(mProfile.getUsername());
513 mPassword.setText(mProfile.getPassword());
514 mMTU.setText(mProfile.getMTU() != null ? mProfile.getMTU().toString() : null);
515 mPort.setText(mProfile.getPort() != null ? mProfile.getPort().toString() : null);
516 mBlockIPv4.setChecked(mProfile.getSplitTunneling() != null ? (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) != 0 : false);
517 mBlockIPv6.setChecked(mProfile.getSplitTunneling() != null ? (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) != 0 : false);
518 useralias = mProfile.getUserCertificateAlias();
519 alias = mProfile.getCertificateAlias();
520 getSupportActionBar().setTitle(mProfile.getName());
521 }
522 else
523 {
524 Log.e(VpnProfileDetailActivity.class.getSimpleName(),
525 "VPN profile with id " + mId + " not found");
526 finish();
527 }
528 }
529
530 mSelectVpnType.setSelection(mVpnType.ordinal());
531
532 /* check if the user selected a user certificate previously */
533 useralias = savedInstanceState == null ? useralias: savedInstanceState.getString(VpnProfileDataSource.KEY_USER_CERTIFICATE);
534 if (useralias != null)
535 {
536 UserCertificateLoader loader = new UserCertificateLoader(this, useralias);
537 mUserCertLoading = useralias;
538 loader.execute();
539 }
540
541 /* check if the user selected a CA certificate previously */
542 alias = savedInstanceState == null ? alias : savedInstanceState.getString(VpnProfileDataSource.KEY_CERTIFICATE);
543 mCheckAuto.setChecked(alias == null);
544 if (alias != null)
545 {
546 X509Certificate certificate = TrustedCertificateManager.getInstance().getCACertificateFromAlias(alias);
547 if (certificate != null)
548 {
549 mCertEntry = new TrustedCertificateEntry(alias, certificate);
550 }
551 else
552 { /* previously selected certificate is not here anymore */
553 showCertificateAlert();
554 mCertEntry = null;
555 }
556 }
557 }
558
559 /**
560 * Get the integer value in the given text box or null if empty
561 *
562 * @param view text box (numeric entry assumed)
563 */
564 private Integer getInteger(EditText view)
565 {
566 String value = view.getText().toString().trim();
567 return value.isEmpty() ? null : Integer.valueOf(value);
568 }
569
570 private class SelectUserCertOnClickListener implements OnClickListener, KeyChainAliasCallback
571 {
572 @Override
573 public void onClick(View v)
574 {
575 String useralias = mUserCertEntry != null ? mUserCertEntry.getAlias() : null;
576 KeyChain.choosePrivateKeyAlias(VpnProfileDetailActivity.this, this, new String[] { "RSA" }, null, null, -1, useralias);
577 }
578
579 @Override
580 public void alias(final String alias)
581 {
582 if (alias != null)
583 { /* otherwise the dialog was canceled, the request denied */
584 try
585 {
586 final X509Certificate[] chain = KeyChain.getCertificateChain(VpnProfileDetailActivity.this, alias);
587 /* alias() is not called from our main thread */
588 runOnUiThread(new Runnable() {
589 @Override
590 public void run()
591 {
592 if (chain != null && chain.length > 0)
593 {
594 mUserCertEntry = new TrustedCertificateEntry(alias, chain[0]);
595 }
596 updateCredentialView();
597 }
598 });
599 }
600 catch (KeyChainException e)
601 {
602 e.printStackTrace();
603 }
604 catch (InterruptedException e)
605 {
606 e.printStackTrace();
607 }
608 }
609 }
610 }
611
612 /**
613 * Load the selected user certificate asynchronously. This cannot be done
614 * from the main thread as getCertificateChain() calls back to our main
615 * thread to bind to the KeyChain service resulting in a deadlock.
616 */
617 private class UserCertificateLoader extends AsyncTask<Void, Void, X509Certificate>
618 {
619 private final Context mContext;
620 private final String mAlias;
621
622 public UserCertificateLoader(Context context, String alias)
623 {
624 mContext = context;
625 mAlias = alias;
626 }
627
628 @Override
629 protected X509Certificate doInBackground(Void... params)
630 {
631 X509Certificate[] chain = null;
632 try
633 {
634 chain = KeyChain.getCertificateChain(mContext, mAlias);
635 }
636 catch (KeyChainException e)
637 {
638 e.printStackTrace();
639 }
640 catch (InterruptedException e)
641 {
642 e.printStackTrace();
643 }
644 if (chain != null && chain.length > 0)
645 {
646 return chain[0];
647 }
648 return null;
649 }
650
651 @Override
652 protected void onPostExecute(X509Certificate result)
653 {
654 if (result != null)
655 {
656 mUserCertEntry = new TrustedCertificateEntry(mAlias, result);
657 }
658 else
659 { /* previously selected certificate is not here anymore */
660 ((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setError("");
661 mUserCertEntry = null;
662 }
663 mUserCertLoading = null;
664 updateCredentialView();
665 }
666 }
667
668 /**
669 * Dialog with notification message if EAP-TNC is used.
670 */
671 public static class TncNoticeDialog extends AppCompatDialogFragment
672 {
673 @Override
674 public Dialog onCreateDialog(Bundle savedInstanceState)
675 {
676 return new AlertDialog.Builder(getActivity())
677 .setTitle(R.string.tnc_notice_title)
678 .setMessage(Html.fromHtml(getString(R.string.tnc_notice_details)))
679 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
680 @Override
681 public void onClick(DialogInterface dialog, int id)
682 {
683 dialog.dismiss();
684 }
685 }).create();
686 }
687 }
688 }