/* * Copyright 2011 yingxinwu.g@gmail.com * * 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 xink.vpn.editor; import xink.vpn.AppException; import xink.vpn.Constants; import xink.vpn.R; import xink.vpn.VpnProfileRepository; import xink.vpn.wrapper.InvalidProfileException; import xink.vpn.wrapper.KeyStore; import xink.vpn.wrapper.VpnProfile; import xink.vpn.wrapper.VpnState; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.Intent; import android.os.Bundle; import android.text.method.PasswordTransformationMethod; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; public abstract class VpnProfileEditor extends Activity { private static final int DESC_FONT_SIZE = 12; private static final int GRAY = 0xFF999999; private EditAction editAction; private VpnProfile profile; private EditText txtVpnName; private EditText txtServer; private EditText txtDnsSuffices; private EditText txtUserName; private EditText txtPassword; private VpnProfileRepository repository; private KeyStore keyStore; private Runnable resumeAction; private Object[] errMsgArgs; // prompt error message @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.vpn_profile_editor); repository = VpnProfileRepository.getInstance(getApplicationContext()); keyStore = new KeyStore(getApplicationContext()); LinearLayout contentView = new LinearLayout(this); contentView.setOrientation(LinearLayout.VERTICAL); initWidgets(contentView); ScrollView containerView = (ScrollView) findViewById(R.id.editorScrollView); containerView.addView(contentView); Intent intent = getIntent(); init(intent); } private void initWidgets(final ViewGroup content) { TextView lblVpnName = new TextView(this); lblVpnName.setText(getString(R.string.vpnname)); content.addView(lblVpnName); txtVpnName = new EditText(this); txtVpnName.setLines(1); txtVpnName.setImeOptions(EditorInfo.IME_ACTION_DONE); content.addView(txtVpnName); TextView lblServer = new TextView(this); lblServer.setText(getString(R.string.server)); content.addView(lblServer); txtServer = new EditText(this); txtServer.setLines(1); txtServer.setImeOptions(EditorInfo.IME_ACTION_NEXT); content.addView(txtServer); initSpecificWidgets(content); TextView lblDnsSuffices = new TextView(this); lblDnsSuffices.setText(getString(R.string.dns_suffices)); content.addView(lblDnsSuffices); TextView lblDnsSufficesDesc = new TextView(this); lblDnsSufficesDesc.setText(getString(R.string.comma_sep)); lblDnsSufficesDesc.setTextColor(GRAY); lblDnsSufficesDesc.setTextSize(TypedValue.COMPLEX_UNIT_DIP, DESC_FONT_SIZE); content.addView(lblDnsSufficesDesc); txtDnsSuffices = new EditText(this); txtDnsSuffices.setLines(1); txtDnsSuffices.setImeOptions(EditorInfo.IME_ACTION_NEXT); content.addView(txtDnsSuffices); TextView lblUserName = new TextView(this); lblUserName.setText(getString(R.string.username)); content.addView(lblUserName); txtUserName = new EditText(this); txtUserName.setLines(1); txtUserName.setImeOptions(EditorInfo.IME_ACTION_NEXT); content.addView(txtUserName); TextView lblPassword = new TextView(this); lblPassword.setText(getString(R.string.password)); content.addView(lblPassword); txtPassword = new EditText(this); txtPassword.setLines(1); txtPassword.setImeOptions(EditorInfo.IME_ACTION_DONE); txtPassword.setTransformationMethod(new PasswordTransformationMethod()); content.addView(txtPassword); initButtons(); } private void initButtons() { Button btnSave = (Button) findViewById(R.id.btnSave); btnSave.setOnClickListener(new OnClickListener() { @Override public void onClick(final View view) { onSave(); } }); Button btnCancel = (Button) findViewById(R.id.btnCancel); btnCancel.setOnClickListener(new OnClickListener() { @Override public void onClick(final View arg0) { onCancel(); } }); } private void init(final Intent intent) { editAction = EditAction.valueOf(intent.getAction()); switch (editAction) { case CREATE: profile = createProfile(); break; case EDIT: String name = (String) intent.getExtras().get(Constants.KEY_VPN_PROFILE_NAME); profile = repository.getProfileByName(name); initViewBinding(); break; default: throw new AppException("failed to init VpnProfileEditor, unknown editAction: " + editAction); //$NON-NLS-1$ } setTitle(profile.getType().getNameRid()); } private void initViewBinding() { txtVpnName.setText(profile.getName()); txtServer.setText(profile.getServerName()); txtDnsSuffices.setText(profile.getDomainSuffices()); txtUserName.setText(profile.getUsername()); txtPassword.setText(profile.getPassword()); doBindToViews(); } protected abstract void doBindToViews(); @Override protected Dialog onCreateDialog(final int id) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setCancelable(true).setMessage(""); //$NON-NLS-1$ return builder.create(); } @Override protected void onPrepareDialog(final int id, final Dialog dialog) { Object[] args = errMsgArgs; errMsgArgs = null; ((AlertDialog) dialog).setMessage(getString(id, args)); } @Override protected void onRestoreInstanceState(final Bundle savedInstanceState) { if (savedInstanceState == null) return; txtVpnName.setText(savedInstanceState.getCharSequence("name")); //$NON-NLS-1$ txtServer.setText(savedInstanceState.getCharSequence("server")); //$NON-NLS-1$ txtDnsSuffices.setText(savedInstanceState.getCharSequence("dns")); //$NON-NLS-1$ txtUserName.setText(savedInstanceState.getCharSequence("user")); //$NON-NLS-1$ txtPassword.setText(savedInstanceState.getCharSequence("password")); //$NON-NLS-1$ } @Override protected void onSaveInstanceState(final Bundle outState) { outState.putCharSequence("name", txtVpnName.getText()); //$NON-NLS-1$ outState.putCharSequence("server", txtServer.getText()); //$NON-NLS-1$ outState.putCharSequence("dns", txtDnsSuffices.getText()); //$NON-NLS-1$ outState.putCharSequence("user", txtUserName.getText()); //$NON-NLS-1$ outState.putCharSequence("password", txtPassword.getText()); //$NON-NLS-1$ } @Override protected void onResume() { super.onResume(); Log.d("xink", "VpnProfileEditor.onResume, check and run resume action"); if (resumeAction != null) { Runnable action = resumeAction; resumeAction = null; runOnUiThread(action); } } protected abstract void doPopulateProfile(); private void onSave() { try { populateProfile(); saveProfile(); } catch (InvalidProfileException e) { promptInvalidProfile(e); } } private void populateProfile() { String name = txtVpnName.getText().toString().trim(); profile.setName(name); profile.setServerName(txtServer.getText().toString().trim()); profile.setDomainSuffices(txtDnsSuffices.getText().toString().trim()); profile.setUsername(txtUserName.getText().toString().trim()); profile.setPassword(txtPassword.getText().toString().trim()); profile.setState(VpnState.IDLE); doPopulateProfile(); repository.checkProfile(profile); } private void saveProfile() { if (unlockKeyStoreIfNeeded()) { if (editAction == EditAction.CREATE) { repository.addVpnProfile(profile); } else { profile.postUpdate(); } prepareResult(); finish(); } } private boolean unlockKeyStoreIfNeeded() { if (!profile.needKeyStoreToSave() || keyStore.isUnlocked()) return true; Log.i("xink", "keystore is locked, unlock it now and redo saving later."); resumeAction = new Runnable() { @Override public void run() { // redo this after unlock activity return saveProfile(); } }; keyStore.unlock(this); return false; } private void prepareResult() { Intent intent = new Intent(); intent.putExtra(Constants.KEY_VPN_PROFILE_NAME, profile.getName()); setResult(RESULT_OK, intent); } private void promptInvalidProfile(final InvalidProfileException e) { errMsgArgs = e.getMessageArgs(); showDialog(e.getMessageCode()); } private void onCancel() { finish(); } protected EditAction getEditAction() { return editAction; } protected abstract void initSpecificWidgets(final ViewGroup content); protected abstract VpnProfile createProfile(); @SuppressWarnings("unchecked") protected <T extends VpnProfile> T getProfile() { return (T) profile; } }