Java tutorial
/* * AndFHEM - Open Source Android application to control a FHEM home automation * server. * * Copyright (c) 2011, Matthias Klass or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU GENERAL PUBLIC LICENSE, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU GENERAL PUBLIC LICENSE * for more details. * * You should have received a copy of the GNU GENERAL PUBLIC LICENSE * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package li.klass.fhem.fragments; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; import android.support.v4.app.FragmentActivity; import android.text.method.PasswordTransformationMethod; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.ImageButton; import android.widget.Spinner; import android.widget.TextView; import java.io.File; import java.io.Serializable; import java.util.Arrays; import java.util.List; import li.klass.fhem.R; import li.klass.fhem.constants.Actions; import li.klass.fhem.constants.BundleExtraKeys; import li.klass.fhem.constants.ResultCodes; import li.klass.fhem.fhem.connection.FHEMServerSpec; import li.klass.fhem.fhem.connection.ServerType; import li.klass.fhem.fragments.core.BaseFragment; import li.klass.fhem.service.intent.ConnectionsIntentService; import li.klass.fhem.ui.FileDialog; import li.klass.fhem.util.FhemResultReceiver; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.collect.Lists.newArrayList; import static li.klass.fhem.constants.BundleExtraKeys.CONNECTION; import static li.klass.fhem.constants.BundleExtraKeys.CONNECTION_ALTERNATE_URL; import static li.klass.fhem.constants.BundleExtraKeys.CONNECTION_CLIENT_CERTIFICATE_PASSWORD; import static li.klass.fhem.constants.BundleExtraKeys.CONNECTION_CLIENT_CERTIFICATE_PATH; import static li.klass.fhem.constants.BundleExtraKeys.CONNECTION_URL; import static li.klass.fhem.constants.BundleExtraKeys.CONNECTION_USERNAME; import static li.klass.fhem.constants.ResultCodes.SUCCESS; import static li.klass.fhem.util.DialogUtil.showAlertDialog; public class ConnectionDetailFragment extends BaseFragment { private static final String TAG = ConnectionDetailFragment.class.getName(); private String connectionId; private boolean isModify = false; private ServerType connectionType; private ConnectionTypeDetailChangedListener detailChangedListener = null; @Override public void setArguments(Bundle args) { super.setArguments(args); if (args.containsKey(BundleExtraKeys.CONNECTION_ID)) { connectionId = args.getString(BundleExtraKeys.CONNECTION_ID); isModify = true; } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); if (view != null) { return view; } view = inflater.inflate(R.layout.connection_detail, container, false); assert (view != null); Spinner connectionTypeSpinner = (Spinner) view.findViewById(R.id.connectionType); if (isModify) { connectionTypeSpinner.setEnabled(false); } final List<ServerType> connectionTypes = getServerTypes(); ArrayAdapter<ServerType> adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_dropdown_item, connectionTypes); connectionTypeSpinner.setAdapter(adapter); connectionTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) { handleConnectionTypeChange(connectionTypes.get(position)); } @Override public void onNothingSelected(AdapterView<?> adapterView) { } }); Button saveButton = (Button) view.findViewById(R.id.save); saveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { handleSave(); } }); return view; } private List<ServerType> getServerTypes() { final List<ServerType> connectionTypes = newArrayList(); connectionTypes.addAll(Arrays.asList(ServerType.values())); connectionTypes.remove(ServerType.DUMMY); return connectionTypes; } @SuppressLint("InflateParams") private void handleConnectionTypeChange(ServerType connectionType) { if (getView() == null) return; this.connectionType = checkNotNull(connectionType); FragmentActivity activity = checkNotNull(getActivity()); LayoutInflater inflater = checkNotNull(activity.getLayoutInflater()); View view; if (connectionType == ServerType.FHEMWEB) { view = inflater.inflate(R.layout.connection_fhemweb, null); handleFHEMWEBView(view); } else if (connectionType == ServerType.TELNET) { view = inflater.inflate(R.layout.connection_telnet, null); } else { throw new IllegalArgumentException("cannot handle connection type " + connectionType); } assert view != null; CheckBox showPasswordCheckbox = (CheckBox) view.findViewById(R.id.showPasswordCheckbox); final EditText passwordView = (EditText) view.findViewById(R.id.password); if (showPasswordCheckbox != null && passwordView != null) { showPasswordCheckbox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { CheckBox radio = (CheckBox) view; boolean checked = radio.isChecked(); if (checked) { passwordView.setTransformationMethod(null); } else { passwordView.setTransformationMethod(PasswordTransformationMethod.getInstance()); } } }); if (isModify) showPasswordCheckbox.setEnabled(false); } CheckBox showCertificatePasswordCheckbox = (CheckBox) view .findViewById(R.id.showCertificatePasswordCheckbox); final EditText passwordClientCertificateView = (EditText) view.findViewById(R.id.clientCertificatePassword); if (showCertificatePasswordCheckbox != null && passwordClientCertificateView != null) { showCertificatePasswordCheckbox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { CheckBox radio = (CheckBox) view; boolean checked = radio.isChecked(); if (checked) { passwordClientCertificateView.setTransformationMethod(null); } else { passwordClientCertificateView .setTransformationMethod(PasswordTransformationMethod.getInstance()); } } }); if (isModify) showCertificatePasswordCheckbox.setEnabled(false); } ViewGroup connectionPreferences = (ViewGroup) checkNotNull(getView()) .findViewById(R.id.connectionPreferences); checkNotNull(connectionPreferences); connectionPreferences.removeAllViews(); connectionPreferences.addView(view); if (detailChangedListener != null) detailChangedListener.onChanged(); } private void handleSave() { Intent intent = new Intent(); intent.setClass(getActivity(), ConnectionsIntentService.class); if (isModify) { intent.setAction(Actions.CONNECTION_UPDATE); intent.putExtra(BundleExtraKeys.CONNECTION_ID, connectionId); } else { intent.setAction(Actions.CONNECTION_CREATE); } intent.putExtra(BundleExtraKeys.CONNECTION_TYPE, connectionType.name()); String name = getTextViewContent(R.id.connectionName); if (enforceNotEmpty(R.string.connectionName, name)) return; intent.putExtra(BundleExtraKeys.CONNECTION_NAME, name); intent.putExtra(BundleExtraKeys.CONNECTION_PASSWORD, getTextViewContent(R.id.password)); switch (connectionType) { case TELNET: if (!handleTelnetSave(intent)) { return; } break; case FHEMWEB: if (!handleFHEMWEBSave(intent)) { return; } break; } intent.putExtra(BundleExtraKeys.RESULT_RECEIVER, new ResultReceiver(new Handler()) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { super.onReceiveResult(resultCode, resultData); if (resultCode != ResultCodes.SUCCESS) { Log.e(TAG, "could not save! resultCode=" + resultCode + ",resultData=" + resultData); return; } Intent intent = new Intent(Actions.BACK); getActivity().sendBroadcast(intent); } }); getActivity().startService(intent); } private void handleFHEMWEBView(View view) { ImageButton setClientCertificate = (ImageButton) view.findViewById(R.id.setClientCertificatePath); setClientCertificate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (getView() == null) return; final TextView clientCertificatePath = (TextView) getView() .findViewById(R.id.clientCertificatePath); File initialPath = new File(clientCertificatePath.getText().toString()); FileDialog fileDialog = new FileDialog(view.getContext(), initialPath); fileDialog.addFileListener(new FileDialog.FileSelectedListener() { @Override public void fileSelected(File file) { clientCertificatePath.setText(file.getAbsolutePath()); } }); fileDialog.showDialog(); } }); } private String getTextViewContent(int id) { View view = getView(); if (view == null) return null; TextView textView = (TextView) view.findViewById(id); if (textView == null) { Log.e(TAG, "cannot find " + id); return null; } return String.valueOf(textView.getText()); } private boolean enforceNotEmpty(int fieldName, String value) { if (value != null && value.trim().length() > 0) { return false; } Context context = getActivity(); String emptyError = context.getString(R.string.connectionEmptyError); String errorMessage = String.format(emptyError, context.getString(fieldName)); showAlertDialog(context, R.string.error, errorMessage); return true; } private boolean handleTelnetSave(Intent intent) { String ip = getTextViewContent(R.id.ip); if (enforceNotEmpty(R.string.connectionIP, ip)) return false; intent.putExtra(BundleExtraKeys.CONNECTION_IP, ip); String port = getTextViewContent(R.id.port); if (enforceNotEmpty(R.string.connectionPort, port)) return false; intent.putExtra(BundleExtraKeys.CONNECTION_PORT, port); return true; } private boolean handleFHEMWEBSave(Intent intent) { if (getView() == null) return false; String url = getTextViewContent(R.id.url); if (enforceNotEmpty(R.string.connectionURL, url)) return false; if (enforceUrlStartsWithHttp(url)) return false; String alternateUrl = getTextViewContent(R.id.alternate_url); if (!isNullOrEmpty(alternateUrl) && enforceUrlStartsWithHttp(alternateUrl)) return false; String username = getTextViewContent(R.id.username); String clientCertificatePath = getTextViewContent(R.id.clientCertificatePath); intent.putExtra(CONNECTION_URL, url).putExtra(CONNECTION_ALTERNATE_URL, alternateUrl) .putExtra(CONNECTION_USERNAME, username) .putExtra(CONNECTION_CLIENT_CERTIFICATE_PATH, clientCertificatePath).putExtra( CONNECTION_CLIENT_CERTIFICATE_PASSWORD, getTextViewContent(R.id.clientCertificatePassword)); return true; } private boolean enforceUrlStartsWithHttp(String url) { if (!url.startsWith("http")) { Context context = getActivity(); String emptyError = context.getString(R.string.connectionUrlHttp); showAlertDialog(context, R.string.error, emptyError); return true; } return false; } @Override public void update(boolean doUpdate) { if (!isModify) { Log.e(TAG, "I can only update if a connection is being modified!"); getActivity().sendBroadcast(new Intent(Actions.DISMISS_EXECUTING_DIALOG)); return; } Intent intent = new Intent(Actions.CONNECTION_GET).setClass(getActivity(), ConnectionsIntentService.class) .putExtra(BundleExtraKeys.CONNECTION_ID, connectionId) .putExtra(BundleExtraKeys.RESULT_RECEIVER, new FhemResultReceiver() { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode != SUCCESS || !resultData.containsKey(CONNECTION)) { return; } Serializable serializable = resultData.getSerializable(CONNECTION); if (!(serializable instanceof FHEMServerSpec)) { Log.e(TAG, "expected an FHEMServerSpec, but got " + serializable); return; } setValuesForCurrentConnection((FHEMServerSpec) serializable); FragmentActivity activity = getActivity(); if (activity != null) activity.sendBroadcast(new Intent(Actions.DISMISS_EXECUTING_DIALOG)); } }); getActivity().startService(intent); } private void setValuesForCurrentConnection(final FHEMServerSpec connection) { final View view = getView(); if (view == null) return; // We do not need to change the type selector here, as the right one is already selected. // We just overwrite values within the edit fields. if (connection.getServerType() == connectionType) { fillDetail(connection); } else { // We have to change the detail view to the one which is right for the current // connection type. However, we do not know when the selection changed listener // of the combo box fires. This is why we register a global listener, which is called // when the new view has been attached to the root view. // Afterwards we can continue with filling the fields with the respective values // of the current connection! detailChangedListener = new ConnectionTypeDetailChangedListener() { @Override public void onChanged() { detailChangedListener = null; fillDetail(connection); } }; Spinner connectionTypeSpinner = (Spinner) view.findViewById(R.id.connectionType); connectionTypeSpinner.setSelection(selectionIndexFor(connection.getServerType()), true); } } private void fillDetail(FHEMServerSpec connection) { setTextViewContent(R.id.connectionName, connection.getName()); switch (connectionType) { case FHEMWEB: fillFHEMWEB(connection); break; case TELNET: fillTelnet(connection); break; } } private int selectionIndexFor(ServerType serverType) { List<ServerType> serverTypes = getServerTypes(); for (int i = 0; i < serverTypes.size(); i++) { if (serverType == serverTypes.get(i)) return i; } return -1; } private void setTextViewContent(int id, String value) { setTextViewContent(getView(), id, value); } private void fillFHEMWEB(FHEMServerSpec connection) { View view = getView(); if (view == null) return; setTextViewContent(view, R.id.url, connection.getUrl()); setTextViewContent(view, R.id.alternate_url, connection.getAlternateUrl()); setTextViewContent(view, R.id.username, connection.getUsername() + ""); setTextViewContent(view, R.id.password, connection.getPassword()); setTextViewContent(view, R.id.clientCertificatePath, connection.getClientCertificatePath()); setTextViewContent(view, R.id.clientCertificatePassword, connection.getClientCertificatePassword()); } private void fillTelnet(FHEMServerSpec connection) { View view = getView(); if (view == null) return; setTextViewContent(view, R.id.ip, connection.getIp()); setTextViewContent(view, R.id.port, connection.getPort() + ""); setTextViewContent(view, R.id.password, connection.getPassword()); } private void setTextViewContent(View view, int id, String value) { if (view == null) return; TextView textView = (TextView) view.findViewById(id); if (textView != null) { textView.setText(value); } } private interface ConnectionTypeDetailChangedListener { void onChanged(); } }