Java tutorial
/* Copyright (c) Sybase, Inc. 2012 All rights reserved. In addition to the license terms set out in the Sybase License Agreement for the Sybase Unwired Platform ("Program"), the following additional or different rights and accompanying obligations and restrictions shall apply to the source code in this file ("Code"). Sybase grants you a limited, non-exclusive, non-transferable, revocable license to use, reproduce, and modify the Code solely for purposes of (i) maintaining the Code as reference material to better understand the operation of the Program, and (ii) development and testing of applications created in connection with your licensed use of the Program. The Code may not be transferred, sold, assigned, sublicensed or otherwise conveyed (whether by operation of law or otherwise) to another party without Sybase's prior written consent. The following provisions shall apply to any modifications you make to the Code: (i) Sybase will not provide any maintenance or support for modified Code or problems that result from use of modified Code; (ii) Sybase expressly disclaims any warranties and conditions, express or implied, relating to modified Code or any problems that result from use of the modified Code; (iii) SYBASE SHALL NOT BE LIABLE FOR ANY LOSS OR DAMAGE RELATING TO MODIFICATIONS MADE TO THE CODE OR FOR ANY DAMAGES RESULTING FROM USE OF THE MODIFIED CODE, INCLUDING, WITHOUT LIMITATION, ANY INACCURACY OF DATA, LOSS OF PROFITS OR DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF SYBASE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; (iv) you agree to indemnify, hold harmless, and defend Sybase from and against any claims or lawsuits, including attorney's fees, that arise from or are related to the modified Code or from use of the modified Code. */ package com.aslanoba.hwc; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Vector; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.app.AlertDialog; import android.app.ListActivity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.text.InputType; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.inputmethod.InputMethodManager; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; import com.aslanoba.hwc.CertListAdapter.CertDetailDialog; import com.sybase.hybridApp.CertDialogDelegator.CertDialogResult; import com.sybase.hybridApp.CertificateManager; import com.sybase.hybridApp.Settings; import com.sybase.hybridApp.Settings.REGISTRATION_METHOD; import com.sybase.hybridApp.amp.Consts; import com.sybase.messaging.common.ClientConfig; import com.sybase.messaging.common.PropertyDefinition; import com.sybase.messaging.common.PropertyID; import com.sybase.messaging.common.persist.Property; import com.sybase.mo.AppLog; import com.sybase.mo.Brand; import com.sybase.mo.MessagingClientException; import com.sybase.mo.MessagingClientLib; import com.sybase.mo.MocaLog; import com.sybase.persistence.SSOCertManager.CertInfo; /** * For initial settings, the save button is disabled until all settings are available. * If save button is disabled, navigate away from the settings screen will discard * all pending settings. * For the following settings, the save button will be enabled if any settings have * been updated. Navigating away from the settings screen will prompt user to save * the pending settings. */ public class SettingsActivity extends ListActivity { // Available iMO registration methods. private RegistrationMethod[] _registrationMethods; /** * Represents a server registration method. */ static class RegistrationMethod { private String _displayLabel; private int _value; /** * Creates a Registration method using the label and value. * @param label the string to be displayed on the UI * @param value the value to be used for this method. */ RegistrationMethod(String label, int value) { _displayLabel = label; _value = value; } /** * Gets the string to be displayed on the UI. * @return the string to be displayed on the UI. */ public String getDisplayLabel() { return _displayLabel; } /** * Get the value for this registrationmethod. * @return the value for this registrationmethod. */ public int getValue() { return _value; } // Returns the labels to be displayed on the UI. private static String[] getLabels(RegistrationMethod[] methods) { List<String> result = new ArrayList<String>(); for (RegistrationMethod method : methods) { result.add(method.getDisplayLabel()); } return result.toArray(new String[0]); } // Returns the values to be used in the list. private static Integer[] getValues(RegistrationMethod[] methods) { List<Integer> result = new ArrayList<Integer>(); for (RegistrationMethod method : methods) { result.add(method.getValue()); } return result.toArray(new Integer[0]); } } /** * Creates the menu items */ @Override public boolean onCreateOptionsMenu(Menu oMenu) { boolean bResult = super.onCreateOptionsMenu(oMenu); m_oMenuItemSave = oMenu.add(0, MENU_SAVE, 0, R.string.Menu_Save).setIcon(android.R.drawable.ic_menu_save); oMenu.add(0, MENU_LOG, 0, R.string.Menu_ShowLog).setIcon(android.R.drawable.ic_menu_view); oMenu.add(0, MENU_RESTART_ENGINE, 0, R.string.Menu_RestartEngine) .setIcon(android.R.drawable.ic_menu_revert); if (CustomizationHelper.getInstance().enablePIN()) { // Only let them change the password when passwords (PINs) are enabled. oMenu.add(0, MENU_CHANGE_PASSWORD, 0, R.string.Menu_Change_Password) .setIcon(android.R.drawable.ic_menu_manage); } if (CustomizationHelper.getInstance().enableAdvancedSettings()) { oMenu.add(0, MENU_ADVANCED, 0, R.string.Menu_Advanced).setIcon(android.R.drawable.ic_menu_compass); } oMenu.add(0, MENU_ABOUT, 0, R.string.Menu_About).setIcon(android.R.drawable.ic_menu_info_details); oMenu.add(0, MENU_APP_CONN_ID, 0, R.string.DeviceIDText); return bResult; } /** * Hook for enabling/disabling menu items. */ @Override public boolean onPrepareOptionsMenu(Menu oMenu) { boolean bIsSettingsComplete = isSettingsComplete(); boolean bValueChanged = valuesChanged(); if (bIsSettingsComplete && (bValueChanged || CustomizationHelper.getInstance().isSettingsSaveButtonAlwaysEnabled())) { // If all of the required fields are filled in and the user has made // changes or customer has customized save button to always be enabled. m_oMenuItemSave.setEnabled(true); } else { m_oMenuItemSave.setEnabled(false); } return true; } /** * Handler called when a menu option is chosen. */ @Override public boolean onOptionsItemSelected(MenuItem oItem) { switch (oItem.getItemId()) { case MENU_LOG: Intent oIntentAppLog = new Intent(this, AppLogActivity.class); startActivity(oIntentAppLog); return true; case MENU_SAVE: menuSave(); return true; case MENU_RESTART_ENGINE: restartEngine(this); return true; case MENU_CHANGE_PASSWORD: startActivity(new Intent(this, ChangePasswordActivity.class)); return true; case MENU_ADVANCED: startActivity(new Intent(this, AdvancedSettingsActivity.class)); return true; case MENU_ABOUT: showAbout(); return true; case MENU_APP_CONN_ID: showAppConnID(this); return true; } return false; } /** * Reports an error when a user-initiated operation fails. * @param x Exception associated with the error */ public static void reportError(MessagingClientException x) { MocaLog.getLog().logMessage("SettingsActivity reports error. ", x, MocaLog.eMocaLogLevel.Normal); } /** * Restarts engine using a worker thread. */ static synchronized void restartEngine(final Context oContext) { // to avoid unpleasant deadlock situations, a restart of the engine is // launched in a separate worker thread Thread tRunner = new Thread(new Runnable() { public void run() { MocaLog.getLog().logMessage("Restarting client engine", MocaLog.eMocaLogLevel.Detailed); MessagingClientLib oClient = MessagingClientLib.getInstance(); try { if (oClient.isConfigured()) { // Reset an cancel data if we restart the engine HybridWebApplication.getInstance().clearRejectionChallengeData(); oClient.restartClient(); } } catch (MessagingClientException x) { reportError(x); } } }); tRunner.start(); } /** * Shows the validation error dialog for an invalid field value. * @param sFieldLabel GUI Label associated with the field being set */ protected void showValidationErrorDialog(String sFieldLabel) { // construct display message String sMsg = getResources().getString(R.string.IDS_MSG_CONN_MOD_VALIDATION_ERROR); // substitute field label for placeholder int iPos = sMsg.indexOf("%1"); if (iPos > 0) { sMsg = sMsg.substring(0, iPos) + sFieldLabel + sMsg.substring(iPos + 2, sMsg.length()); } // show it new AlertDialog.Builder(this).setTitle(R.string.SaveConnectionChangeWarningTitle).setMessage(sMsg) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dialog.dismiss(); } }).show(); } /** * Validate a setting item * @param oItem * @return */ protected boolean validateField(SettingsListItem oItem) { //validate input parameter's id and value type boolean bValid = true; try { if (oItem.m_bPassword) bValid = oItem.m_oValue != null && oItem.m_oValue.toString().length() > 0; else { PropertyDefinition.validatePropertyValue(oItem.m_iPropId, oItem.m_oValue); if (oItem.m_iPropId == PropertyID.CONNECTION_USER_NAME) validateUsernameRule(oItem.m_oValue.toString()); } } catch (Exception x) { bValid = false; } return bValid; } /** * Checks current UI to see if refresh of all data may occur * @return True if data may be cleared */ protected boolean isRefreshAll() { String sServer = ""; String sUser = ""; String sActCode = ""; String sFarmId = ""; int iPort = 0; for (int i = 0; i < m_lstSettings.size(); i++) { SettingsListItem oItem = m_lstSettings.get(i); switch (oItem.m_iPropId) { case PropertyID.CONNECTION_SERVER_NAME: sServer = oItem.getTrimmedValue(); break; case PropertyID.CONNECTION_USER_NAME: sUser = oItem.getTrimmedValue(); break; case PropertyID.CONNECTION_SERVER_PORT: iPort = (Integer) oItem.m_oValue; break; case PropertyID.CONNECTION_ACTIVATION_CODE: sActCode = oItem.getTrimmedValue(); break; case PropertyID.CONNECTION_FARM_ID: sFarmId = oItem.getTrimmedValue(); break; } } return isRefreshAll(sServer, iPort, sFarmId, sUser, sActCode); } /** * Returns true if the changes may trigger a refresh of all data */ protected boolean isRefreshAll(String sServer, int iPort, String sFarmId, String sUser, String sActCode) { boolean bRefreshAll = false; ClientConfig oClientConfig = new ClientConfig(); if (oClientConfig.isInitialized()) { if (oClientConfig.checkPropertyChanged(PropertyID.CONNECTION_SERVER_NAME, sServer, null)) bRefreshAll = true; else if (oClientConfig.checkPropertyChanged(PropertyID.CONNECTION_SERVER_PORT, Integer.toString(iPort), null)) bRefreshAll = true; else if (oClientConfig.checkPropertyChanged(PropertyID.CONNECTION_FARM_ID, sFarmId, null)) bRefreshAll = true; else if (oClientConfig.checkPropertyChanged(PropertyID.CONNECTION_USER_NAME, sUser, null)) bRefreshAll = true; else if (oClientConfig.checkPropertyChanged(PropertyID.CONNECTION_ACTIVATION_CODE, sActCode, null)) bRefreshAll = true; } return bRefreshAll; } /** * Exclude the cases: * String caseA = "shep@"; * String caseB = "@shep"; * String caseC = "@"; * @param usernameValue * @return */ private boolean validateUsernameRule(String usernameValue) { if (usernameValue.endsWith("@") || usernameValue.startsWith("@")) { throw new IllegalArgumentException(); } return true; } /** * Saves the values from the edit fields into the configuration store. * the data should already been validated during user input, so no validation is needed */ private void save() { Log.v("ALPER", "Save triggered"); String sServer = ""; String sUser = ""; String sActCode = ""; String sFarmId = ""; String sUrlSuffix = ""; String sPassword = ""; int iPort = 0; Boolean bUseHTTPS = false; int iRegMethod = 0; String sCertFilePath = ""; for (int i = 0; i < m_lstSettings.size(); i++) { SettingsListItem oItem = m_lstSettings.get(i); // only save valid field. restore the invalid if (validateField(oItem)) { oItem.m_oOrigValue = oItem.m_oValue; } else { oItem.m_oValue = oItem.m_oOrigValue; } switch (oItem.m_iPropId) { case PropertyID.CONNECTION_SERVER_NAME: sServer = oItem.getTrimmedValue(); break; case PropertyID.CONNECTION_USER_NAME: sUser = oItem.getTrimmedValue(); break; case PropertyID.CONNECTION_SERVER_PORT: iPort = (Integer) oItem.m_oValue; break; case PropertyID.CONNECTION_ACTIVATION_CODE: sActCode = oItem.getTrimmedValue(); break; case PropertyID.CONNECTION_FARM_ID: sFarmId = oItem.getTrimmedValue(); break; case PropertyID.ADVANCED_RELAY_SVR_URL_TEMPLATE: sUrlSuffix = oItem.getTrimmedValue(); break; case PropertyID.CONNECTION_PASSWORD: sPassword = (String) oItem.m_oValue; break; case PropertyID.CONNECTION_USE_HTTPS: bUseHTTPS = (Boolean) oItem.m_oValue; break; case PropertyID.CONNECTION_AUTO_REGISTRATION_HINT: iRegMethod = (Integer) oItem.m_oValue; break; case PropertyID.CONNECTION_CERTIFICATE_PATH: sCertFilePath = (String) oItem.m_oValue; break; } } saveConnectionProperties(sServer, iPort, sFarmId, sUser, sActCode, sPassword, sUrlSuffix, bUseHTTPS, iRegMethod, sCertFilePath); } protected void saveConnectionProperties(final String sServer, final int iPort, final String sFarmId, final String sUser, final String sActCode, final String sPassword, final String sUrlSuffix, final Boolean bUseHTTPS, final int iRegMethod, final String sCertFilePath) { // Don't block ui while saving settings new Thread(new Runnable() { public void run() { try { String password; // Save state before changing config boolean bIsConfiguredBefore = MessagingClientLib.getInstance().isConfigured(); if (iRegMethod == Settings.REGISTRATION_METHOD.AFARIA) { // reuse "User Name" as "Common Name", without security conf. // e.g. john@aTemplate, john is the common name. // john@sap.com@aTemplate, john@sap.com is the common name // other cases like '@aTemplate', 'john@', '@' are excluded by function validateUsernameRule String commonName = sUser.indexOf('@') == -1 ? sUser : // without '@' (sUser.substring(sUser.lastIndexOf('@')).indexOf('.') != -1) ? sUser : // email-like username sUser.substring(0, sUser.lastIndexOf('@')); // email-like username with a template appended String challengeCode = sPassword; //reuse "Password" as "Challenge Code" password = getCertificateFromAfaria(commonName, challengeCode); } else if (iRegMethod == Settings.REGISTRATION_METHOD.CERTIFICATE) { password = m_sCertString; } else { password = sPassword; } // Needs to be called before startClient, // because the customer's http headers should be applied to the connections of starting the client. CustomizationHelper.getInstance().setHttpHeaders(); Settings.saveConnectionProperties(sServer, iPort, sFarmId, sUser, sActCode, password, sUrlSuffix, bUseHTTPS, iRegMethod, sCertFilePath); if (MessagingClientLib.getInstance().isConfigured()) { if (!bIsConfiguredBefore) { // First time configured. Start client Log.v("ALPER", "SettingsActivity started"); new StartClientTask(SettingsActivity.this).execute(SettingsActivity.this); } else { // Restart client to pick up any changes Log.v("ALPER", "Restart client to pick up any changes"); MessagingClientLib.getInstance().restartClient(); } } } catch (MessagingClientException x) { AppLog.getAppLog().logError(x.getErrorCode(), null); reportError(x); } } }).start(); } /** * Returns true if the current values on the screen * do not match the originals when we entered the screen. * @return true iff the editable values have changed */ protected boolean valuesChanged() { boolean bChanged = false; for (int i = 0; i < m_lstSettings.size(); i++) { SettingsListItem oItem = m_lstSettings.get(i); if (!oItem.m_oOrigValue.equals(oItem.m_oValue)) { bChanged = true; break; } } return bChanged; } /** * Save operation called from menu. If anything has been changed, puts up * the warning dialog and does the real save if the user says to do so. * bFromBackButton: if called from backButton, the handler needs to call onBackPressed to dismiss * the current screen. */ public void menuSave() { if (isRefreshAll()) { // Warn user that data may be cleared new AlertDialog.Builder(this).setTitle(R.string.SaveConnectionChangeWarningTitle) .setMessage(R.string.SaveConnectionChangeWarningText) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // If we changed the connection settings we should clear // settings related to this server. HybridWebApplication.getInstance().clearRejectionChallengeData(); Log.v("ALPER", "menuSave"); save(); // do the real save } }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }).show(); } else { // For initial config, there is no need to refresh the client or nag // the user about it Log.v("ALPER", "initial save"); save(); } } /** * Populates the available registration methods. */ private void populateRegistrationMethods() { _registrationMethods = new RegistrationMethod[4]; _registrationMethods[0] = new RegistrationMethod( getResources().getString(R.string.Label_RegistrationMethodManual), REGISTRATION_METHOD.NO_PREF_DEFAULT_MANUAL); _registrationMethods[1] = new RegistrationMethod( getResources().getString(R.string.Label_RegistrationMethodAutomaticPassword), REGISTRATION_METHOD.AUTOMATIC); _registrationMethods[2] = new RegistrationMethod( getResources().getString(R.string.Label_RegistrationMethodAutomaticAfariaCertificate), REGISTRATION_METHOD.AFARIA); _registrationMethods[3] = new RegistrationMethod( getResources().getString(R.string.Label_RegistrationMethodAutomaticLocalCertificate), REGISTRATION_METHOD.CERTIFICATE); } /** * Get registration method value from selected index of dropdown list component * @param regMethodValue * @return */ private int toSelectedIndex(int regMethodValue) { // default value to be used if the regMethodValue does not match available method values. int result = REGISTRATION_METHOD.NO_PREF_DEFAULT_MANUAL; for (int i = 0; i < _registrationMethods.length; i++) { if (_registrationMethods[i].getValue() == regMethodValue) { result = i; break; } } return result; } /* * Loads values from the config into the settings list */ public void load() { m_lstSettings.clear(); // Determine if we are doing auto device registration Property oProp = CustomizationHelper.getInstance().getDefaultConnectionRegistrationMethod(); m_iRegistrationMethod = Integer.parseInt(oProp.getValue().toString()); m_lstSettings.add(new ChoiceSettingsListItem(getString(R.string.Label_RegistrationMethod), toSelectedIndex(m_iRegistrationMethod), RegistrationMethod.getLabels(_registrationMethods), RegistrationMethod.getValues(_registrationMethods), PropertyID.CONNECTION_AUTO_REGISTRATION_HINT, oProp.getDisplayMode() == ClientConfig.DISPLAY_MODE_LOCKED)); oProp = CustomizationHelper.getInstance().getDefaultConnectionLocalCertPath(); m_sCertFilePath = oProp.getValue().toString(); m_lstSettings.add( new SettingsListItem(null, m_sCertFilePath, PropertyID.CONNECTION_CERTIFICATE_PATH, false, false)); oProp = CustomizationHelper.getInstance().getDefaultConnectionServerName(); m_lstSettings.add(new SettingsListItem(getString(R.string.Label_ServerName), oProp.getValue(), PropertyID.CONNECTION_SERVER_NAME, oProp.getDisplayMode() == ClientConfig.DISPLAY_MODE_LOCKED, false)); oProp = CustomizationHelper.getInstance().getDefaultConnectionServerPort(); m_lstSettings.add(new SettingsListItem(getString(R.string.Label_Port), oProp.getValue(), PropertyID.CONNECTION_SERVER_PORT, oProp.getDisplayMode() == ClientConfig.DISPLAY_MODE_LOCKED, false)); oProp = CustomizationHelper.getInstance().getDefaultConnectionFarmId(); m_lstSettings.add(new SettingsListItem(getString(R.string.Label_FarmId), oProp.getValue(), PropertyID.CONNECTION_FARM_ID, oProp.getDisplayMode() == ClientConfig.DISPLAY_MODE_LOCKED, false)); oProp = CustomizationHelper.getInstance().getDefaultConnectionUserName(); m_lstSettings.add(new SettingsListItem(getString(R.string.Label_UserName), oProp.getValue(), PropertyID.CONNECTION_USER_NAME, oProp.getDisplayMode() == ClientConfig.DISPLAY_MODE_LOCKED, false)); oProp = CustomizationHelper.getInstance().getDefaultConnectionActivationCode(); m_lstSettings.add(new SettingsListItem(getString(R.string.Label_ActivationCode), oProp.getValue(), PropertyID.CONNECTION_ACTIVATION_CODE, oProp.getDisplayMode() == ClientConfig.DISPLAY_MODE_LOCKED, false)); // Password for auto device registration oProp = CustomizationHelper.getInstance().getDefaultConnectionPassword(); m_lstSettings.add(new SettingsListItem(getString(R.string.Label_Password), oProp.getValue(), PropertyID.CONNECTION_PASSWORD, false, true)); oProp = CustomizationHelper.getInstance().isDefaultConnectionHTTPS(); int iSelectedIndex = oProp.getValue().equals(Boolean.TRUE) ? 1 : 0; // Order is HTTP, HTTPS if property true we are using HTTPS m_lstSettings.add(new ChoiceSettingsListItem(getString(R.string.Label_Protocol), iSelectedIndex, new String[] { "HTTP", "HTTPS" }, new Boolean[] { Boolean.FALSE, Boolean.TRUE }, PropertyID.CONNECTION_USE_HTTPS, oProp.getDisplayMode() == ClientConfig.DISPLAY_MODE_LOCKED)); oProp = CustomizationHelper.getInstance().getDefaultConnectionUrlSuffix(); m_lstSettings.add(new SettingsListItem(getString(R.string.Label_URLSuffix), oProp.getValue(), PropertyID.ADVANCED_RELAY_SVR_URL_TEMPLATE, oProp.getDisplayMode() == ClientConfig.DISPLAY_MODE_LOCKED, false)); CustomizationHelper.getInstance().onPostSettingsLoad(this); } /** * Displays the local certificate selection dialog if the local certificate provisioning is chosen * using a local provisioning file & the registration method selection is enabled. */ void checkLocalCertificateSelectionRequired() { if (m_iRegistrationMethod == Settings.REGISTRATION_METHOD.CERTIFICATE && CustomizationHelper.getInstance().isConnectionRegistrationMethodVisible()) { if (m_sCertFilePath == null || m_sCertFilePath.trim().equals("")) { runOnUiThread(new Runnable() { public void run() { handleLocalCertificateSelection(); } }); } } } /** * Reload list control from existing settings list. */ public void refreshList() { // Filter the settings list to only include the settings the customer wants // to show. m_filteredSettings = new Vector<SettingsListItem>(); for (int i = 0; i < m_lstSettings.size(); i++) { SettingsListItem oItem = m_lstSettings.get(i); switch (oItem.m_iPropId) { case PropertyID.CONNECTION_AUTO_REGISTRATION_HINT: if (CustomizationHelper.getInstance().isConnectionRegistrationMethodVisible()) { m_filteredSettings.add(oItem); } break; case PropertyID.CONNECTION_SERVER_NAME: if (CustomizationHelper.getInstance().isConnectionServerNameVisible()) { m_filteredSettings.add(oItem); } break; case PropertyID.CONNECTION_USER_NAME: if (CustomizationHelper.getInstance().isConnectionUserNameVisible()) { m_filteredSettings.add(oItem); } break; case PropertyID.CONNECTION_SERVER_PORT: if (CustomizationHelper.getInstance().isConnectionServerPortVisible()) { m_filteredSettings.add(oItem); } break; case PropertyID.CONNECTION_ACTIVATION_CODE: if (CustomizationHelper.getInstance().isConnectionActivationCodeVisible()) { m_filteredSettings.add(oItem); } break; case PropertyID.CONNECTION_FARM_ID: if (CustomizationHelper.getInstance().isConnectionFarmIdVisible()) { m_filteredSettings.add(oItem); } break; case PropertyID.ADVANCED_RELAY_SVR_URL_TEMPLATE: if (CustomizationHelper.getInstance().isConnectionUrlSuffixVisible()) { m_filteredSettings.add(oItem); } break; case PropertyID.CONNECTION_PASSWORD: if (CustomizationHelper.getInstance().isConnectionPasswordVisible()) { m_filteredSettings.add(oItem); } break; case PropertyID.CONNECTION_USE_HTTPS: if (CustomizationHelper.getInstance().isConnectionHTTPSVisible()) { m_filteredSettings.add(oItem); } break; } } // Create placeholders in adapter data structure so it knows how many items // to display. We will fill in the actual display data in getView() based on the // position ArrayList<HashMap<String, String>> lst = new ArrayList<HashMap<String, String>>(); for (int i = 0; i < m_filteredSettings.size(); i++) lst.add(new HashMap<String, String>()); // While we are setting a list adapter, we are only using it to hold the // structure of the list. We will actually override its data in getView() // before displaying it. setListAdapter(new SimpleAdapter(this, lst, R.layout.settings_row, new String[] { "text1", "text2" }, new int[] { android.R.id.text1, android.R.id.text2 }) { /** * Override for the getView() method. This allows us to alter the display * attributes on a line-by-line basis. We also use this opportunity to * fill in the text fields with the right values. */ @Override public View getView(int iPos, View oConvertView, ViewGroup oParent) { SettingsListItem oItem = m_filteredSettings.get(iPos); setItemState(oItem); // create view for this row from xml layout file View oView = m_oInflater.inflate(R.layout.settings_row, null); // for some reason, the logic in this method call seems to be reversed, // but it works this way oView.setClickable(oItem.m_bLocked); // set up the label text view TextView tvLabel = (TextView) oView.findViewById(android.R.id.text1); if (tvLabel != null) { tvLabel.setText(getItemLabel(oItem)); tvLabel.setEnabled(!oItem.m_bLocked); } // set up the value text view TextView tvValue = (TextView) oView.findViewById(android.R.id.text2); if (tvValue != null) { String sValue = oItem.getDisplayText(); if (oItem.m_bPassword) tvValue.setText(sValue.length() > 0 ? "******" : ""); else tvValue.setText(sValue); tvValue.setEnabled(!oItem.m_bLocked); // Please note that attempting to setInputType here to mask the password value crashes // HTC desire HD device. To avoid that we are working around by displaying asterix in // password field. } return oView; } }); } /** * Determines whether a setting item is blocked by registration method and field * * @return */ private void setItemState(SettingsListItem item) { boolean isLocked = false; switch (m_iRegistrationMethod) { case Settings.REGISTRATION_METHOD.MANUAL: case Settings.REGISTRATION_METHOD.NO_PREF_DEFAULT_MANUAL: { if (item.m_iPropId == PropertyID.CONNECTION_PASSWORD) { isLocked = true; break; } else { isLocked = false; break; } } case Settings.REGISTRATION_METHOD.AUTOMATIC: if (item.m_iPropId == PropertyID.CONNECTION_ACTIVATION_CODE) { isLocked = true; break; } else { isLocked = false; break; } case Settings.REGISTRATION_METHOD.AFARIA: if (item.m_iPropId == PropertyID.CONNECTION_ACTIVATION_CODE) { isLocked = true; break; } else { isLocked = false; break; } case Settings.REGISTRATION_METHOD.CERTIFICATE: if (item.m_iPropId == PropertyID.CONNECTION_ACTIVATION_CODE) { isLocked = true; break; } else if (item.m_iPropId == PropertyID.CONNECTION_PASSWORD) { isLocked = true; break; } else if (item.m_iPropId == PropertyID.CONNECTION_USER_NAME) { isLocked = false; break; } else { isLocked = false; break; } } item.m_bLocked = isLocked; } /** * Translate labels if registration methods is Afaria Field IDs are reused. * * @param item * @return new label */ private String getItemLabel(SettingsListItem item) { String label = item.m_sLabel; if (m_iRegistrationMethod == Settings.REGISTRATION_METHOD.AFARIA) { if (item.m_iPropId == PropertyID.CONNECTION_USER_NAME) label = getString(R.string.Label_CommanName); else if (item.m_iPropId == PropertyID.CONNECTION_PASSWORD) label = getString(R.string.Label_ChallengeCode); } return label; } /** * Update SettingListItem model's value by new certificate info * * @param newPath * New local certificate file path * @param certInfo * New local certificate info */ private void updateCertSettings(String newPath, CertInfo certInfo) { if (newPath != null) for (SettingsListItem oItem : m_lstSettings) { if (oItem.m_iPropId == PropertyID.CONNECTION_CERTIFICATE_PATH) { oItem.m_oOrigValue = oItem.m_oValue; oItem.m_oValue = newPath; continue; } else if (oItem.m_iPropId == PropertyID.CONNECTION_USER_NAME) { if (certInfo.sSubjectCN == null) { MocaLog.getAmpHostLog().logMessage("certificate is invalid - certInfo.sSubjectCN is null", MocaLog.eMocaLogLevel.Normal); // show the dialog new AlertDialog.Builder(this).setCancelable(false) .setTitle(getResources().getString(R.string.CertSelectorScreenInvalidPassword)) .setIcon(android.R.drawable.stat_notify_error) .setPositiveButton(android.R.string.ok, null).show(); } oItem.m_oOrigValue = oItem.m_oValue; oItem.m_oValue = certInfo.sSubjectCN == null ? "" : certInfo.sSubjectCN; } } refreshList(); } /** * Get certificate string generated by Afaria * * @param commonName * @param challengeCode * @return certificate string */ private String getCertificateFromAfaria(String commonName, String challengeCode) { CertificateManager cm = new CertificateManager(null, this, null); final CertInfo certInfo = new CertInfo(); String certJsonString = cm.getSignedCertificateFromAfaria(commonName, challengeCode, certInfo); String certString = ""; if (certJsonString != null) try { final Activity currentScreen = this; runOnUiThread(new Runnable() { public void run() { new CertDetailDialog(currentScreen, certInfo).setTitleFor("Afaria Certificate").show(); } }); JSONObject certJson = new JSONObject(certJsonString); certString = certJson.getString(Consts.JCON_CERT_SIGNED_CERTIFICATE); } catch (JSONException e) { MocaLog.getAmpHostLog().logMessage( "Error occured while parsing JSON certificate - " + e.getMessage(), e, MocaLog.eMocaLogLevel.Normal); } return certString; } /** * Show local certificate picker * * @param activity * @param selectedCertFilePath * @param certDialogResult */ private void pickCertificateFromFile(Activity activity, final String selectedCertFilePath, CertDialogResult certDialogResult) { CertificateManager cm = new CertificateManager(null, this, null); cm.showCertificatePicker(null, selectedCertFilePath, certDialogResult); } /** * Get certificate string from local certificate * * @param certificateFilePath * @param password * @return certificate string */ private String getCertificateFromFile(String certificateFilePath, String password, CertInfo certInfo) { CertificateManager cm = new CertificateManager(null, this, null); String certJsonString = cm.getSignedCertificateFromFile(certificateFilePath, password, certInfo); String certString = ""; if (certJsonString != null) { try { JSONObject certJson = new JSONObject(certJsonString); certString = certJson.getString(Consts.JCON_CERT_SIGNED_CERTIFICATE); } catch (JSONException e) { MocaLog.getAmpHostLog().logMessage( "Error occured while parsing JSON certificate - " + e.getMessage(), e, MocaLog.eMocaLogLevel.Normal); } } return certString; } /** Called when the activity is first created. * Usually, the input parameter oSavedInstanceState is null. However, if the activity has already * started, and then sent to the background, and destroyed due to low memory, when the * settingsActivity is brought to foreground again, the parameter will contain the pending ui * change, we need to apply those change to UI to follow the Android design guide line. */ @Override public void onCreate(Bundle oSavedInstanceState) { super.onCreate(oSavedInstanceState); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); m_oInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); populateRegistrationMethods(); } @Override protected void onDestroy() { super.onDestroy(); } /** * Resume the ui, also update the settings list to get the updated configuration. * The method is called when the activity is brought to foreground from background * We need to reload the connection settings from config store since while the activity is * in background the messaging client may update the connection settings (like url * prefix), so that when checking whether the settings have been changed, it can compare * the ui value with the updated new configured values. */ @Override protected void onResume() { super.onResume(); // check to see if we need to redirect to the password entry or // change screen if (PasswordPolicyMgr.redirect(this)) return; // if the user made changes to the settings but didn't save, we // don't want to call load() since that will override their unsaved changes // However, we do want to call load() otherwise so changes made from SCC // will be visible when this activity is resumed. if (m_lstSettings.size() == 0 || !valuesChanged()) { Log.v("ALPER", " onResume Save "); load(); } // Register challenge listener HybridWebApplication.getInstance().setChallengeListener(m_oChallengeListener); if (!m_bProvisionAttempted && !MessagingClientLib.getInstance().isConfigured()) { m_bProvisionAttempted = true; // Okay to init and provision now that password is set // (initInstance does provisioning for us), which now happens in new StartClientTask(this).execute(this); } // the configuration data may be changed while the screen is hide // reload it. Note we do not change the UI to avoid losing any pending changes refreshList(); } /** * Save the current value in ui control for restoring later. In most cases, the saved state is * not used. However, if the activity is collected by OS due to low memory, then the saved * state will be used in onCreate method with the Bundle parameter, so that any pending change * in UI will be kept and restored. */ @Override protected void onSaveInstanceState(Bundle outState) { outState.clear(); //save pending settings in UI for (int i = 0; i < m_lstSettings.size(); i++) { SettingsListItem oItem = m_lstSettings.get(i); if (!oItem.m_oOrigValue.equals(oItem.m_oValue)) { if (oItem.m_oValue instanceof Integer) outState.putInt(String.valueOf(oItem.m_iPropId), (Integer) oItem.m_oValue); else if (oItem.m_oValue instanceof Boolean) outState.putBoolean(String.valueOf(oItem.m_iPropId), (Boolean) oItem.m_oValue); else outState.putString(String.valueOf(oItem.m_iPropId), oItem.m_oValue.toString()); } } } /** * Determines if all the required settings are filled in. * @return True if required settings provided */ protected boolean isSettingsComplete() { boolean bIsComplete = true; for (int i = 0; i < m_lstSettings.size(); i++) { // Check server name, username, activation code, and port number. // The user name needs to be checked even if it is locked. The user name is only // locked when using certificate registration, but even then when the certificate // is selected it will populate the user name (and this value is required) SettingsListItem oItem = m_lstSettings.get(i); // PropertyID.CONNECTION_PASSWORD is used in two places. As password // for Automatic(Password) and as challenge code for Automatic(Afaria). // It is only required for Automatic(Password). if (oItem.m_iPropId == PropertyID.CONNECTION_USER_NAME || (!oItem.m_bLocked && (oItem.m_iPropId == PropertyID.CONNECTION_SERVER_NAME || oItem.m_iPropId == PropertyID.CONNECTION_SERVER_PORT || oItem.m_iPropId == PropertyID.CONNECTION_ACTIVATION_CODE || (m_iRegistrationMethod == Settings.REGISTRATION_METHOD.AUTOMATIC && oItem.m_iPropId == PropertyID.CONNECTION_PASSWORD)))) { if (null == oItem.m_oValue || (oItem.m_oValue instanceof String && ((String) oItem.m_oValue).length() <= 0)) { bIsComplete = false; break; } } } return bIsComplete; } /** * Called to handle back key to prompt user to save any pending settings in UI */ protected void handleUIPendingSettings() { //need to prompt user AlertDialog.Builder dlgWarning = new AlertDialog.Builder(this); dlgWarning.setTitle(R.string.UnsavedChangesWarningTitle); ClientConfig oConfig = new ClientConfig(); if (oConfig.isInitialized()) dlgWarning.setMessage(R.string.SaveConnectionChangeWarningText); else dlgWarning.setMessage(R.string.UnsavedChangesWarningMessage); dlgWarning.setPositiveButton(R.string.UnsavedChangesSaveButton, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { Log.v("ALPER", "handleUIPendingSettings"); save(); SettingsActivity.this.finish(); } }); dlgWarning.setNeutralButton(R.string.UnsavedChangesDiscardButton, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { SettingsActivity.this.finish(); } }); dlgWarning.show(); } /** * Handle back key, need to prompt user to save pending change */ @Override public void onBackPressed() { // if value changed and settings are complete, then prompt the user to save the change if (valuesChanged() && isSettingsComplete()) { handleUIPendingSettings(); } else { Intent oIntent = new Intent(this, UiHybridAppMessagesScreen.class); startActivity(oIntent); } } @Override protected void onPause() { // Unregister challenge listener HybridWebApplication.getInstance().setChallengeListener(null); super.onPause(); } private void showAppConnID(final Context oContext) { // TODO Auto-generated method stub MessagingClientLib oClient = MessagingClientLib.getInstance(); // show it new AlertDialog.Builder(this).setTitle(R.string.DeviceIDText).setMessage(oClient.getDeviceID()) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { dialog.dismiss(); } }).show(); } /** * Shows the About box */ private void showAbout() { // use an xml layout View oView = m_oInflater.inflate(R.layout.aboutbox, null); if (CustomizationHelper.getInstance().customAbout(this)) { // Show custom about. } else { // Show our about. // fill in field values from Brand constants and config TextView tv = (TextView) oView.findViewById(R.id.TextViewCopyright); if (tv != null) tv.setText(Brand.OEM_COPYRIGHT); tv = (TextView) oView.findViewById(R.id.TextViewCompany); if (tv != null) tv.setText(Brand.OEM_FORMAL_COMPANY_NAME); tv = (TextView) oView.findViewById(R.id.TextViewApplication); if (tv != null) tv.setText(Brand.OEM_ROBIE_PRODUCT); // get version info from package tv = (TextView) oView.findViewById(R.id.TextViewVersion); if (tv != null) { try { tv.setText(getPackageManager().getPackageInfo(getPackageName(), 0).versionName); } catch (NameNotFoundException e) { // If we can't find our own package... something is really wrong... MocaLog.getLog().logMessage("Failed to find package " + e.getMessage(), MocaLog.eMocaLogLevel.Normal); } } // show the dialog new AlertDialog.Builder(this).setTitle(Brand.OEM_ROBIE_PRODUCT).setIcon(R.drawable.icon) .setNeutralButton(android.R.string.ok, null).setView(oView).show(); } } /** * Click handler for the list items. Will create the appropriate edit * box for the clicked setting. */ @Override public void onListItemClick(ListView oParent, View v, int iPos, long id) { final ListView oParentListView = oParent; final SettingsListItem oItem = m_filteredSettings.get(iPos); if (oItem instanceof ChoiceSettingsListItem) { final ChoiceSettingsListItem oChoiceItem = (ChoiceSettingsListItem) oItem; // Show dialog new AlertDialog.Builder(SettingsActivity.this) .setTitle(getString(R.string.Label_Edit) + " " + oItem.m_sLabel) .setSingleChoiceItems(oChoiceItem.m_asText, oChoiceItem.m_iSelectedItem, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { oChoiceItem.select(whichButton); if (oItem.m_iPropId == PropertyID.CONNECTION_AUTO_REGISTRATION_HINT) { m_iRegistrationMethod = (Integer) oChoiceItem.m_oValue; if (m_iRegistrationMethod == Settings.REGISTRATION_METHOD.CERTIFICATE) { handleLocalCertificateSelection(); } } // refresh in ui refreshList(); oParentListView.invalidate(); // Close dialog dialog.dismiss(); } }) .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Close dialog dialog.dismiss(); } }).show(); } else { // inflate the xml layout final View oView = m_oInflater.inflate(R.layout.settings_edit, null); TextView tv = (TextView) oView.findViewById(R.id.TextViewEditSettings); if (tv != null) tv.setText(getItemLabel(oItem)); // set value tv = (TextView) oView.findViewById(R.id.EditTextEditSettings); if (tv != null) tv.setText(oItem.m_oValue.toString()); // special input validator for integers if (oItem.m_oValue instanceof Integer) tv.setInputType(InputType.TYPE_CLASS_NUMBER); if (oItem.m_bPassword) tv.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); // show dialog, valid value when user clicks ok button new AlertDialog.Builder(this).setTitle(getString(R.string.Label_Edit) + " " + getItemLabel(oItem)) .setView(oView).setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { TextView tv = (TextView) oView.findViewById(R.id.EditTextEditSettings); String sText = tv.getText().toString(); if (oItem.m_oValue instanceof Integer) oItem.m_oValue = Integer.parseInt(sText); else if (oItem.m_oValue instanceof Boolean) oItem.m_oValue = Boolean.parseBoolean(sText); else oItem.m_oValue = sText; InputMethodManager imm = (InputMethodManager) getSystemService( Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(tv.getWindowToken(), 0); //validate the input value if (!validateField(oItem)) { // validation failed, reset to original value oItem.m_oValue = oItem.m_oOrigValue; showValidationErrorDialog(oItem.m_sLabel); } else { // refresh in ui refreshList(); oParentListView.invalidate(); } } }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { TextView tv = (TextView) oView.findViewById(R.id.EditTextEditSettings); InputMethodManager imm = (InputMethodManager) getSystemService( Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(tv.getWindowToken(), 0); dialog.dismiss(); } }).show(); } } @Override public void onUserInteraction() { // check to see if we need to redirect to the password entry or // change screen PasswordPolicyMgr.redirect(this); PasswordPolicyMgr.resetIdleCounter(); } /** * Displays a dialog to select local certificates. */ private void handleLocalCertificateSelection() { final CertInfo certInfo = new CertInfo(); pickCertificateFromFile(SettingsActivity.this, m_sCertFilePath, new CertDialogResult() { /** * Updates the certificate settings using the user selection. */ @Override public void setResult(String newCertFilePath, String newPassword) { m_sCertString = getCertificateFromFile(newCertFilePath, newPassword, certInfo); m_sCertFilePath = newCertFilePath; updateCertSettings(newCertFilePath, certInfo); } }); } public boolean m_bInitializedAfterPassword = false; // menu items static final int MENU_LOG = 1; static final int MENU_CHANGE_PASSWORD = 2; static final int MENU_SAVE = 3; static final int MENU_RESTART_ENGINE = 4; static final int MENU_ADVANCED = 5; static final int MENU_ABOUT = 6; static final int MENU_APP_CONN_ID = 7; // reference to "save" item on menu. Used for enabling/disabling //TODO: not need this variable, should get it dynamically based on menuid private MenuItem m_oMenuItemSave = null; /** * Class/struct used to hold data about one settings field in the * list view. * */ class SettingsListItem { SettingsListItem(String sLabel, Object oValue, int iPropId, boolean bLocked, boolean bPassword) { m_sLabel = sLabel; m_oValue = oValue; m_oOrigValue = oValue; m_iPropId = iPropId; m_bLocked = bLocked; m_bPassword = bPassword; } String getDisplayText() { return m_oValue.toString(); } String getTrimmedValue() { return ((String) m_oValue).trim(); } String m_sLabel; // label text to be displayed for this item Object m_oValue; // current editable value for this item Object m_oOrigValue; // original value loaded from config int m_iPropId; // associated iMO property ID boolean m_bLocked; // true if this value is locked for editing boolean m_bPassword; // true if the password field } class ChoiceSettingsListItem extends SettingsListItem { public ChoiceSettingsListItem(String sLabel, int iSelectedIndex, String[] asText, Object[] aoValues, int iPropId, boolean bLocked) { super(sLabel, aoValues[iSelectedIndex], iPropId, bLocked, false); m_iSelectedItem = iSelectedIndex; m_asText = asText; m_aoValues = aoValues; } String getDisplayText() { return m_asText[m_iSelectedItem]; } public void select(int iIndex) { m_iSelectedItem = iIndex; m_oValue = m_aoValues[iIndex]; } private String[] m_asText; // Display text for the choices private Object[] m_aoValues; // Values for the choices private int m_iSelectedItem = 0; } protected boolean m_bProvisionAttempted = false; // Flag to prevent trying to provision too much. // list of the settings items we are displaying in the list view and // potentially editing protected Vector<SettingsListItem> m_lstSettings = new Vector<SettingsListItem>(); protected Vector<SettingsListItem> m_filteredSettings = new Vector<SettingsListItem>(); // inflater for converting xml files to views protected LayoutInflater m_oInflater; // Receiver for challenges private ChallengeListener m_oChallengeListener = new ChallengeListener(this); // Current registration method private int m_iRegistrationMethod = Settings.REGISTRATION_METHOD.MANUAL; //Default // Certificate Base64 string, from Afaria or local certificate private String m_sCertString; // Local certificate path if registration method is "Local Certificate" private String m_sCertFilePath; /** * A class to launch setting screen from activity stack. */ public static final class SettingScreenLauncher { private boolean m_bResumedScreen; /** * Try to launch setting screen if client configuration is not set. * * @param oActivity Activity instance. */ public void tryLaunchSettingScreen(Activity oActivity) { if (null == oActivity) { return; } // when message client is not configured // to start setting screen if screen resumed from the top of activity stack if (m_bResumedScreen) { if (!MessagingClientLib.getInstance().isConfigured()) { Intent oIntent = new Intent(oActivity, SettingsActivity.class); oIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); oActivity.startActivity(oIntent); } } m_bResumedScreen = true; } } }