diff options
-rw-r--r-- | app/src/main/java/org/connectbot/HostEditorFragment.java | 584 | ||||
-rw-r--r-- | app/src/main/java/org/connectbot/bean/HostBean.java | 2 | ||||
-rw-r--r-- | app/src/main/res/layout/fragment_host_editor.xml | 461 | ||||
-rw-r--r-- | app/src/main/res/values-v11/styles.xml | 13 | ||||
-rw-r--r-- | app/src/main/res/values/dimens.xml | 25 | ||||
-rw-r--r-- | app/src/main/res/values/styles.xml | 36 |
6 files changed, 764 insertions, 357 deletions
diff --git a/app/src/main/java/org/connectbot/HostEditorFragment.java b/app/src/main/java/org/connectbot/HostEditorFragment.java index 56022d7..fe6f838 100644 --- a/app/src/main/java/org/connectbot/HostEditorFragment.java +++ b/app/src/main/java/org/connectbot/HostEditorFragment.java @@ -18,6 +18,7 @@ package org.connectbot; import java.util.ArrayList; +import java.util.Iterator; import java.util.Map; import android.content.ContentValues; @@ -29,19 +30,18 @@ import android.os.Parcelable; import android.support.design.widget.TextInputLayout; import android.support.v4.app.Fragment; import android.support.v7.widget.AppCompatCheckBox; +import android.support.v7.widget.PopupMenu; import android.support.v7.widget.SwitchCompat; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ImageButton; import android.widget.SeekBar; -import android.widget.Spinner; import android.widget.TextView; import org.connectbot.bean.HostBean; @@ -59,7 +59,10 @@ public class HostEditorFragment extends Fragment { private static final String ARG_PUBKEY_VALUES = "pubkeyValues"; private static final String ARG_QUICKCONNECT_STRING = "quickConnectString"; + // Note: The "max" value for mFontSizeSeekBar is 32. If these font values change, this value + // must be changed in the SeekBar's XML. private static final int MINIMUM_FONT_SIZE = 8; + private static final int MAXIMUM_FONT_SIZE = 40; // The host being edited. private HostBean mHost; @@ -68,10 +71,6 @@ public class HostEditorFragment extends Fragment { // Lists because Bundles can only contain ArrayLists, not general Lists. private ArrayList<String> mPubkeyNames; private ArrayList<String> mPubkeyValues; - - // Whether the host is being created for the first time (as opposed to an existing one being - // edited). - private boolean mIsCreating; // The listener for changes to this host. private Listener mListener; @@ -86,17 +85,24 @@ public class HostEditorFragment extends Fragment { // first field, etc. private boolean mUriFieldEditInProgress = false; - // Values for the colors displayed in the color Spinner. These are not necessarily the same as - // the text in the Spinner because the text is localized while these values are not. + // Names and values for the colors displayed in the color Spinner. Names are localized, while + // values are the same across languages, so the values are the ones saved to the database. + private TypedArray mColorNames; private TypedArray mColorValues; - // Likewise, but for SSH auth agent values. + // Likewise, but for SSH auth agent. + private TypedArray mSshAuthNames; private TypedArray mSshAuthValues; - // Likewise, but for DEL key values. + // Likewise, but for DEL key. + private TypedArray mDelKeyNames; private TypedArray mDelKeyValues; - private Spinner mTransportSpinner; + // A map from Charset display name to Charset value (i.e., unique ID for the Charset). + private Map<String, String> mCharsetData; + + private View mTransportItem; + private TextView mTransportText; private TextInputLayout mQuickConnectContainer; private EditText mQuickConnectField; private ImageButton mExpandCollapseButton; @@ -108,20 +114,29 @@ public class HostEditorFragment extends Fragment { private View mPortContainer; private EditText mPortField; private EditText mNicknameField; - private Spinner mColorSelector; - private TextView mFontSizeText; + private View mColorItem; + private TextView mColorText; + private EditText mFontSizeText; private SeekBar mFontSizeSeekBar; - private Spinner mPubkeySpinner; - private View mUseSshConfirmationContainer; + private View mPubkeyItem; + private TextView mPubkeyText; + private View mDelKeyItem; + private TextView mDelKeyText; + private View mEncodingItem; + private TextView mEncodingText; + private View mUseSshAuthItem; private SwitchCompat mUseSshAuthSwitch; - private AppCompatCheckBox mSshAuthConfirmationCheckbox; + private View mUseSshConfirmationItem; + private AppCompatCheckBox mUseSshConfirmationCheckbox; + private View mCompressionItem; private SwitchCompat mCompressionSwitch; + private View mStartShellItem; private SwitchCompat mStartShellSwitch; + private View mStayConnectedItem; private SwitchCompat mStayConnectedSwitch; + private View mCloseOnDisconnectItem; private SwitchCompat mCloseOnDisconnectSwitch; private EditText mPostLoginAutomationField; - private Spinner mDelKeySpinner; - private Spinner mEncodingSpinner; public static HostEditorFragment newInstance( HostBean existingHost, ArrayList<String> pubkeyNames, ArrayList<String> pubkeyValues) { @@ -147,7 +162,6 @@ public class HostEditorFragment extends Fragment { Bundle bundle = savedInstanceState == null ? getArguments() : savedInstanceState; Parcelable existingHostParcelable = bundle.getParcelable(ARG_EXISTING_HOST); - mIsCreating = existingHostParcelable == null; if (existingHostParcelable != null) { mHost = HostBean.fromContentValues((ContentValues) existingHostParcelable); mHost.setId(bundle.getLong(ARG_EXISTING_HOST_ID)); @@ -166,59 +180,28 @@ public class HostEditorFragment extends Fragment { Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_host_editor, container, false); - mTransportSpinner = (Spinner) view.findViewById(R.id.transport_selector); - String[] transportNames = TransportFactory.getTransportNames(); - ArrayAdapter<String> transportSelection = new ArrayAdapter<>( - getActivity(), android.R.layout.simple_spinner_item, transportNames); - transportSelection.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mTransportSpinner.setAdapter(transportSelection); - for (int i = 0; i < transportNames.length; i++) { - if (transportNames[i].equals(mHost.getProtocol())) { - mTransportSpinner.setSelection(i); - break; - } - } - mTransportSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + mTransportItem = view.findViewById(R.id.protocol_item); + mTransportItem.setOnClickListener(new View.OnClickListener() { @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - String protocol = (String) mTransportSpinner.getSelectedItem(); - if (protocol == null) { - // During initialization, protocol can be null before the list of dropdown items - // has been generated. Return early in that case. - return; - } - - mHost.setProtocol(protocol); - mHost.setPort(TransportFactory.getTransport(protocol).getDefaultPort()); - handleHostChange(); - - mQuickConnectContainer.setHint( - TransportFactory.getFormatHint(protocol, getActivity())); - - // Different protocols have different field types, so show only the fields needed. - if (SSH.getProtocolName().equals(protocol)) { - mUsernameContainer.setVisibility(View.VISIBLE); - mHostnameContainer.setVisibility(View.VISIBLE); - mPortContainer.setVisibility(View.VISIBLE); - mExpandCollapseButton.setVisibility(View.VISIBLE); - } else if (Telnet.getProtocolName().equals(protocol)) { - mUsernameContainer.setVisibility(View.GONE); - mHostnameContainer.setVisibility(View.VISIBLE); - mPortContainer.setVisibility(View.VISIBLE); - mExpandCollapseButton.setVisibility(View.VISIBLE); - } else { - // Local protocol has only one field, so no need to show the URI parts - // container. - setUriPartsContainerExpanded(false); - mExpandCollapseButton.setVisibility(View.INVISIBLE); + public void onClick(View v) { + PopupMenu menu = new PopupMenu(getActivity(), v); + for (String name : TransportFactory.getTransportNames()) { + menu.getMenu().add(name); } - } - - @Override - public void onNothingSelected(AdapterView<?> parent) { + menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + setTransportType( + item.getTitle().toString(), /* setDefaultPortInModel */ true); + return true; + } + }); + menu.show(); } }); + mTransportText = (TextView) view.findViewById(R.id.protocol_text); + mQuickConnectContainer = (TextInputLayout) view.findViewById(R.id.quickconnect_field_container); @@ -237,15 +220,8 @@ public class HostEditorFragment extends Fragment { @Override public void afterTextChanged(Editable s) { - if (mTransportSpinner.getSelectedItem() == null) { - // During initialization, protocol can be null before the list of dropdown items - // has been generated. Return early in that case. - return; - } - if (!mUriFieldEditInProgress) { - applyQuickConnectString( - s.toString(), (String) mTransportSpinner.getSelectedItem()); + applyQuickConnectString(s.toString(), mHost.getProtocol()); mUriFieldEditInProgress = true; mUsernameField.setText(mHost.getUsername()); @@ -281,39 +257,63 @@ public class HostEditorFragment extends Fragment { mPortField.setText(Integer.toString(mHost.getPort())); mPortField.addTextChangedListener(new HostTextFieldWatcher(HostDatabase.FIELD_HOST_PORT)); + setTransportType(mHost.getProtocol(), /* setDefaultPortInModel */ false); + mNicknameField = (EditText) view.findViewById(R.id.nickname_field); mNicknameField.setText(mHost.getNickname()); mNicknameField.addTextChangedListener( new HostTextFieldWatcher(HostDatabase.FIELD_HOST_NICKNAME)); - mColorSelector = (Spinner) view.findViewById(R.id.color_selector); + mColorItem = view.findViewById(R.id.color_item); + mColorItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + PopupMenu menu = new PopupMenu(getActivity(), v); + for (int i = 0; i < mColorNames.length(); i++) { + menu.getMenu().add(mColorNames.getText(i)); + } + menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + for (int i = 0; i < mColorNames.length(); i++) { + if (item.getTitle().toString().equals(mColorNames.getText(i).toString())) { + mHost.setColor(mColorValues.getText(i).toString()); + mColorText.setText(mColorNames.getText(i)); + return true; + } + } + return false; + } + }); + menu.show(); + } + }); + + mColorText = (TextView) view.findViewById(R.id.color_text); for (int i = 0; i < mColorValues.length(); i++) { - if (mColorValues.getString(i).equals(mHost.getColor())) { - mColorSelector.setSelection(i); + if (mColorValues.getText(i).toString().equals(mHost.getColor())) { + mColorText.setText(mColorNames.getText(i)); break; } } - mColorSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - mHost.setColor(mColorValues.getString(position)); - handleHostChange(); - } + mFontSizeText = (EditText) view.findViewById(R.id.font_size_text); + mFontSizeText.setText(Integer.toString(mHost.getFontSize())); + mFontSizeText.addTextChangedListener( + new HostTextFieldWatcher(HostDatabase.FIELD_HOST_FONTSIZE)); + mFontSizeText.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override - public void onNothingSelected(AdapterView<?> parent) { + public void onFocusChange(View v, boolean hasFocus) { + if (!hasFocus) + mFontSizeText.setText(Integer.toString(mHost.getFontSize())); } }); - mFontSizeText = (TextView) view.findViewById(R.id.font_size_text); mFontSizeSeekBar = (SeekBar) view.findViewById(R.id.font_size_bar); mFontSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - int fontSize = MINIMUM_FONT_SIZE + progress; - mHost.setFontSize(fontSize); - handleHostChange(); - mFontSizeText.setText(Integer.toString(fontSize)); + setFontSize(MINIMUM_FONT_SIZE + progress); } @Override @@ -326,116 +326,292 @@ public class HostEditorFragment extends Fragment { }); mFontSizeSeekBar.setProgress(mHost.getFontSize() - MINIMUM_FONT_SIZE); - mPubkeySpinner = (Spinner) view.findViewById(R.id.pubkey_spinner); - final String[] pubkeyNames = new String[mPubkeyNames.size()]; - mPubkeyNames.toArray(pubkeyNames); - ArrayAdapter<String> pubkeySelection = new ArrayAdapter<String>( - getActivity(), android.R.layout.simple_spinner_item, pubkeyNames); - pubkeySelection.setDropDownViewResource( - android.R.layout.simple_spinner_dropdown_item); - mPubkeySpinner.setAdapter(pubkeySelection); - for (int i = 0; i < pubkeyNames.length; i++) { - if (mHost.getPubkeyId() == Integer.parseInt(mPubkeyValues.get(i))) { - mPubkeySpinner.setSelection(i); + mPubkeyItem = view.findViewById(R.id.pubkey_item); + mPubkeyItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + PopupMenu menu = new PopupMenu(getActivity(), v); + for (String name : mPubkeyNames) { + menu.getMenu().add(name); + } + menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + for (int i = 0; i < mPubkeyNames.size(); i++) { + if (mPubkeyNames.get(i).equals(item.getTitle())) { + mHost.setPubkeyId(Long.parseLong(mPubkeyValues.get(i))); + mPubkeyText.setText(mPubkeyNames.get(i)); + return true; + } + } + return false; + } + }); + menu.show(); + } + }); + + mPubkeyText = (TextView) view.findViewById(R.id.pubkey_text); + for (int i = 0; i < mPubkeyValues.size(); i++) { + if (mHost.getPubkeyId() == Long.parseLong(mPubkeyValues.get(i))) { + mPubkeyText.setText(mPubkeyNames.get(i)); break; } } - mPubkeySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + + mDelKeyItem = view.findViewById(R.id.delkey_item); + mDelKeyItem.setOnClickListener(new View.OnClickListener() { @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - mHost.setPubkeyId(Integer.parseInt(mPubkeyValues.get(position))); - handleHostChange(); + public void onClick(View v) { + PopupMenu menu = new PopupMenu(getActivity(), v); + for (int i = 0; i < mDelKeyNames.length(); i++) { + menu.getMenu().add(mDelKeyNames.getText(i)); + } + menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + for (int i = 0; i < mDelKeyNames.length(); i++) { + if (mDelKeyNames.getText(i).equals(item.getTitle())) { + mHost.setDelKey(mDelKeyValues.getText(i).toString()); + mDelKeyText.setText(mDelKeyNames.getText(i)); + return true; + } + } + return false; + } + }); + menu.show(); + } + }); + + mDelKeyText = (TextView) view.findViewById(R.id.delkey_text); + for (int i = 0; i < mDelKeyValues.length(); i++) { + if (mDelKeyValues.getText(i).toString().equals(mHost.getDelKey())) { + mDelKeyText.setText(mDelKeyNames.getText(i)); + break; + } + } + + mEncodingItem = view.findViewById(R.id.encoding_item); + mEncodingItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + PopupMenu menu = new PopupMenu(getActivity(), v); + for (String displayName : mCharsetData.keySet()) { + menu.getMenu().add(displayName); + } + menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + for (String displayName : mCharsetData.keySet()) { + if (displayName.equals(item.getTitle())) { + mHost.setEncoding(mCharsetData.get(displayName)); + mEncodingText.setText(displayName); + return true; + } + } + return false; + } + }); + menu.show(); } + }); + + // The encoding text is initialized in setCharsetData() because Charset data is not always + // available when this fragment is created. + mEncodingText = (TextView) view.findViewById(R.id.encoding_text); + mUseSshAuthItem = view.findViewById(R.id.use_ssh_auth_item); + mUseSshAuthItem.setOnClickListener(new View.OnClickListener() { @Override - public void onNothingSelected(AdapterView<?> parent) { + public void onClick(View v) { + mUseSshAuthSwitch.toggle(); } }); - mUseSshConfirmationContainer = view.findViewById(R.id.ssh_confirmation_container); mUseSshAuthSwitch = (SwitchCompat) view.findViewById(R.id.use_ssh_auth_switch); - mSshAuthConfirmationCheckbox = + mUseSshAuthSwitch.setChecked(mHost.getUseAuthAgent() != null && + !mHost.getUseAuthAgent().equals(mSshAuthValues.getString(0))); + mUseSshAuthSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + processSshAuthChange(); + } + }); + + mUseSshConfirmationItem = view.findViewById(R.id.ssh_auth_confirmation_item); + mUseSshConfirmationItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mUseSshConfirmationCheckbox.toggle(); + } + }); + + mUseSshConfirmationCheckbox = (AppCompatCheckBox) view.findViewById(R.id.ssh_auth_confirmation_checkbox); - CompoundButton.OnCheckedChangeListener authSwitchListener = new CompoundButton.OnCheckedChangeListener() { + mUseSshConfirmationCheckbox.setChecked(mHost.getUseAuthAgent() != null && + mHost.getUseAuthAgent().equals(mSshAuthValues.getString(1))); + mUseSshConfirmationCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - mUseSshConfirmationContainer.setVisibility( - mUseSshAuthSwitch.isChecked() ? View.VISIBLE : View.GONE); - if (mUseSshAuthSwitch.isChecked()) { - mHost.setUseAuthAgent( - mSshAuthConfirmationCheckbox.isChecked() ? - /* require confirmation */ mSshAuthValues.getString(1) : - /* don't require confirmation */ mSshAuthValues.getString(2)); - } else { - mHost.setUseAuthAgent(/* don't use */ mSshAuthValues.getString(0)); - } - handleHostChange(); + processSshAuthChange(); } - }; - if (mHost.getUseAuthAgent() == null || - mHost.getUseAuthAgent().equals(mSshAuthValues.getString(0))) { - mUseSshAuthSwitch.setChecked(false); - mSshAuthConfirmationCheckbox.setChecked(false); - } else { - mUseSshAuthSwitch.setChecked(true); - mUseSshConfirmationContainer.setVisibility(View.VISIBLE); - mSshAuthConfirmationCheckbox.setChecked( - mHost.getUseAuthAgent().equals(mSshAuthValues.getString(1))); - } - mUseSshAuthSwitch.setOnCheckedChangeListener(authSwitchListener); - mSshAuthConfirmationCheckbox.setOnCheckedChangeListener(authSwitchListener); + }); + + processSshAuthChange(); + + mCompressionItem = view.findViewById(R.id.compression_item); + mCompressionItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mCompressionSwitch.toggle(); + } + }); mCompressionSwitch = (SwitchCompat) view.findViewById(R.id.compression_switch); mCompressionSwitch.setChecked(mHost.getCompression()); - mCompressionSwitch.setOnCheckedChangeListener( - new HostSwitchWatcher(HostDatabase.FIELD_HOST_COMPRESSION)); + mCompressionSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + mHost.setCompression(isChecked); + handleHostChange(); + } + }); + + mStartShellItem = view.findViewById(R.id.start_shell_item); + mStartShellItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mStartShellSwitch.toggle(); + } + }); mStartShellSwitch = (SwitchCompat) view.findViewById(R.id.start_shell_switch); mStartShellSwitch.setChecked(mHost.getWantSession()); - mStartShellSwitch.setOnCheckedChangeListener( - new HostSwitchWatcher(HostDatabase.FIELD_HOST_WANTSESSION)); + mStartShellSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + mHost.setWantSession(isChecked); + handleHostChange(); + } + }); + + mStayConnectedItem = view.findViewById(R.id.stay_connected_item); + mStayConnectedItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mStayConnectedSwitch.toggle(); + } + }); mStayConnectedSwitch = (SwitchCompat) view.findViewById(R.id.stay_connected_switch); mStayConnectedSwitch.setChecked(mHost.getStayConnected()); - mStayConnectedSwitch.setOnCheckedChangeListener( - new HostSwitchWatcher(HostDatabase.FIELD_HOST_STAYCONNECTED)); + mStayConnectedSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + mHost.setStayConnected(isChecked); + handleHostChange(); + } + }); + + mCloseOnDisconnectItem = view.findViewById(R.id.close_on_disconnect_item); + mCloseOnDisconnectItem.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mCloseOnDisconnectSwitch.toggle(); + } + }); mCloseOnDisconnectSwitch = (SwitchCompat) view.findViewById(R.id.close_on_disconnect_switch); mCloseOnDisconnectSwitch.setChecked(mHost.getQuickDisconnect()); - mCloseOnDisconnectSwitch.setOnCheckedChangeListener( - new HostSwitchWatcher(HostDatabase.FIELD_HOST_QUICKDISCONNECT)); + mCloseOnDisconnectSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + mHost.setQuickDisconnect(isChecked); + handleHostChange(); + } + }); mPostLoginAutomationField = (EditText) view.findViewById(R.id.post_login_automation_field); mPostLoginAutomationField.setText(mHost.getPostLogin()); mPostLoginAutomationField.addTextChangedListener( new HostTextFieldWatcher(HostDatabase.FIELD_HOST_POSTLOGIN)); - mDelKeySpinner = (Spinner) view.findViewById(R.id.del_key_spinner); - for (int i = 0; i < mDelKeyValues.length(); i++) { - if (mHost.getDelKey().equals(mDelKeyValues.getString(i))) { - mDelKeySpinner.setSelection(i); - break; - } + setUriPartsContainerExpanded(mIsUriEditorExpanded); + + return view; + } + + /** + * @param protocol The protocol to set. + * @param setDefaultPortInModel True if the model's port should be updated to the default port + * for the given protocol. + */ + private void setTransportType(String protocol, boolean setDefaultPortInModel) { + mHost.setProtocol(protocol); + if (setDefaultPortInModel) + mHost.setPort(TransportFactory.getTransport(protocol).getDefaultPort()); + handleHostChange(); + + mTransportText.setText(protocol); + + mQuickConnectContainer.setHint( + TransportFactory.getFormatHint(protocol, getActivity())); + + // Different protocols have different field types, so show only the fields needed. + if (SSH.getProtocolName().equals(protocol)) { + mUsernameContainer.setVisibility(View.VISIBLE); + mHostnameContainer.setVisibility(View.VISIBLE); + mPortContainer.setVisibility(View.VISIBLE); + mExpandCollapseButton.setVisibility(View.VISIBLE); + } else if (Telnet.getProtocolName().equals(protocol)) { + mUsernameContainer.setVisibility(View.GONE); + mHostnameContainer.setVisibility(View.VISIBLE); + mPortContainer.setVisibility(View.VISIBLE); + mExpandCollapseButton.setVisibility(View.VISIBLE); + } else { + // Local protocol has only one field, so no need to show the URI parts + // container. + setUriPartsContainerExpanded(false); + mExpandCollapseButton.setVisibility(View.INVISIBLE); } - mDelKeySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - mHost.setDelKey(mDelKeyValues.getString(position)); - handleHostChange(); - } + } - @Override - public void onNothingSelected(AdapterView<?> parent) { - } - }); + private void setFontSize(int fontSize) { + if (fontSize < MINIMUM_FONT_SIZE) + fontSize = MINIMUM_FONT_SIZE; - mEncodingSpinner = (Spinner) view.findViewById(R.id.encoding_spinner); - // The spinner is initialized in setCharsetData() because Charset data is not always - // available when this fragment is created. + if (fontSize > MAXIMUM_FONT_SIZE) + fontSize = MAXIMUM_FONT_SIZE; - setUriPartsContainerExpanded(mIsUriEditorExpanded); + mHost.setFontSize(fontSize); - return view; + if (mFontSizeSeekBar.getProgress() + MINIMUM_FONT_SIZE != fontSize) { + mFontSizeSeekBar.setProgress(fontSize - MINIMUM_FONT_SIZE); + } + + if (!mFontSizeText.isFocused() && + Integer.parseInt(mFontSizeText.getText().toString()) != fontSize) { + mFontSizeText.setText(Integer.toString(fontSize)); + } + + handleHostChange(); + } + + private void processSshAuthChange() { + mUseSshConfirmationItem.setVisibility( + mUseSshAuthSwitch.isChecked() ? View.VISIBLE : View.GONE); + + if (mUseSshAuthSwitch.isChecked()) { + mHost.setUseAuthAgent( + mUseSshConfirmationCheckbox.isChecked() ? + /* require confirmation */ mSshAuthValues.getString(1) : + /* don't require confirmation */ mSshAuthValues.getString(2)); + } else { + mHost.setUseAuthAgent(/* don't use */ mSshAuthValues.getString(0)); + } + + handleHostChange(); } @Override @@ -449,8 +625,11 @@ public class HostEditorFragment extends Fragment { // Now that the fragment is attached to an Activity, fetch the arrays from the attached // Activity's resources. + mColorNames = getResources().obtainTypedArray(R.array.list_colors); mColorValues = getResources().obtainTypedArray(R.array.list_color_values); + mSshAuthNames = getResources().obtainTypedArray(R.array.list_authagent); mSshAuthValues = getResources().obtainTypedArray(R.array.list_authagent_values); + mDelKeyNames = getResources().obtainTypedArray(R.array.list_delkey); mDelKeyValues = getResources().obtainTypedArray(R.array.list_delkey_values); } @@ -458,8 +637,11 @@ public class HostEditorFragment extends Fragment { public void onDetach() { super.onDetach(); mListener = null; + mColorNames.recycle(); mColorValues.recycle(); + mSshAuthNames.recycle(); mSshAuthValues.recycle(); + mDelKeyNames.recycle(); mDelKeyValues.recycle(); } @@ -482,32 +664,17 @@ public class HostEditorFragment extends Fragment { * Charset). */ public void setCharsetData(final Map<String, String> data) { - if (mEncodingSpinner != null) { - final String[] encodingNames = new String[data.keySet().size()]; - data.keySet().toArray(encodingNames); - ArrayAdapter<String> encodingSelection = new ArrayAdapter<String>( - getActivity(), android.R.layout.simple_spinner_item, encodingNames); - encodingSelection.setDropDownViewResource( - android.R.layout.simple_spinner_dropdown_item); - mEncodingSpinner.setAdapter(encodingSelection); - for (int i = 0; i < encodingNames.length; i++) { - if (mHost.getEncoding() != null && - mHost.getEncoding().equals(data.get(encodingNames[i]))) { - mEncodingSpinner.setSelection(i); - break; + mCharsetData = data; + + if (mEncodingText != null) { + Iterator it = data.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<String, String> pair = (Map.Entry) it.next(); + if (pair.getValue().equals(mHost.getEncoding())) { + mEncodingText.setText(pair.getKey()); + return; } } - mEncodingSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - mHost.setEncoding(data.get(encodingNames[position])); - handleHostChange(); - } - - @Override - public void onNothingSelected(AdapterView<?> parent) { - } - }); } } @@ -560,16 +727,14 @@ public class HostEditorFragment extends Fragment { * notified. */ private void handleHostChange() { - String protocol = (String) mTransportSpinner.getSelectedItem(); String quickConnectString = mQuickConnectField.getText().toString(); - if (protocol == null || protocol.equals("") || - quickConnectString == null || quickConnectString.equals("")) { + if (quickConnectString == null || quickConnectString.equals("")) { // Invalid protocol and/or string, so don't do anything. mListener.onHostInvalidated(); return; } - Uri uri = TransportFactory.getUri(protocol, quickConnectString); + Uri uri = TransportFactory.getUri(mHost.getProtocol(), quickConnectString); if (uri == null) { // Valid string, but does not accurately describe a URI. mListener.onHostInvalidated(); @@ -581,8 +746,8 @@ public class HostEditorFragment extends Fragment { } public interface Listener { - public void onValidHostConfigured(HostBean host); - public void onHostInvalidated(); + void onValidHostConfigured(HostBean host); + void onHostInvalidated(); } private class HostTextFieldWatcher implements TextWatcher { @@ -617,6 +782,14 @@ public class HostEditorFragment extends Fragment { mHost.setNickname(text); } else if (HostDatabase.FIELD_HOST_POSTLOGIN.equals(mFieldType)) { mHost.setPostLogin(text); + } else if (HostDatabase.FIELD_HOST_FONTSIZE.equals(mFieldType)) { + int fontSize = HostBean.DEFAULT_FONT_SIZE; + try { + fontSize = Integer.parseInt(text); + } catch (NumberFormatException e) { + } finally { + setFontSize(fontSize); + } } else { throw new RuntimeException("Invalid field type."); } @@ -640,29 +813,4 @@ public class HostEditorFragment extends Fragment { HostDatabase.FIELD_HOST_PORT.equals(fieldType); } } - - private class HostSwitchWatcher implements CompoundButton.OnCheckedChangeListener { - - private final String mFieldType; - - public HostSwitchWatcher(String fieldType) { - mFieldType = fieldType; - } - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (HostDatabase.FIELD_HOST_COMPRESSION.equals(mFieldType)) { - mHost.setCompression(isChecked); - } else if (HostDatabase.FIELD_HOST_WANTSESSION.equals(mFieldType)) { - mHost.setWantSession(isChecked); - } else if (HostDatabase.FIELD_HOST_STAYCONNECTED.equals(mFieldType)) { - mHost.setStayConnected(isChecked); - } else if (HostDatabase.FIELD_HOST_QUICKDISCONNECT.equals(mFieldType)) { - mHost.setQuickDisconnect(isChecked); - } else { - throw new RuntimeException("Invalid field type."); - } - handleHostChange(); - } - } } diff --git a/app/src/main/java/org/connectbot/bean/HostBean.java b/app/src/main/java/org/connectbot/bean/HostBean.java index 438713e..a31f9ec 100644 --- a/app/src/main/java/org/connectbot/bean/HostBean.java +++ b/app/src/main/java/org/connectbot/bean/HostBean.java @@ -33,7 +33,7 @@ import android.net.Uri; public class HostBean extends AbstractBean { public static final String BEAN_NAME = "host"; - private static final int DEFAULT_FONT_SIZE = 10; + public static final int DEFAULT_FONT_SIZE = 10; /* Database fields */ private long id = -1; diff --git a/app/src/main/res/layout/fragment_host_editor.xml b/app/src/main/res/layout/fragment_host_editor.xml index 5e809d8..e4a2094 100644 --- a/app/src/main/res/layout/fragment_host_editor.xml +++ b/app/src/main/res/layout/fragment_host_editor.xml @@ -19,7 +19,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" + android:fillViewport="true" tools:context="org.connectbot.HostEditorFragment" > @@ -29,31 +30,42 @@ android:orientation="vertical" > - <LinearLayout - android:layout_width="wrap_content" + <RelativeLayout + android:id="@+id/protocol_item" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_marginStart="4dp" - android:layout_marginLeft="4dp" - android:layout_marginBottom="4dp" > + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/protocol_spinner_label" + style="@style/ListItemIcon" + /> + <TextView + android:id="@+id/protocol_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/protocol_spinner_label" - android:textSize="12sp" + style="@style/ListItemFirstLineText.WithIcon" /> - <Spinner - android:id="@+id/transport_selector" + <TextView + android:id="@+id/protocol_text" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_below="@id/protocol_title" + tools:text="ssh" + style="@style/ListItemSecondLineText.WithIcon" /> - </LinearLayout> + </RelativeLayout> <LinearLayout + android:layout_marginLeft="68dp" + android:layout_marginStart="68dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:animateLayoutChanges="true" @@ -96,8 +108,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:layout_marginLeft="56dp" - android:layout_marginStart="56dp" + android:layout_marginLeft="68dp" + android:layout_marginStart="68dp" + android:layout_marginRight="48dp" + android:layout_marginEnd="48dp" android:visibility="gone" android:animateLayoutChanges="true" tools:ignore="UnusedAttribute" @@ -156,118 +170,219 @@ </LinearLayout> - <View style="@style/Divider" - /> - - <android.support.design.widget.TextInputLayout + <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > - <EditText - android:id="@+id/nickname_field" + <android.support.design.widget.TextInputLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:maxLines="1" - android:inputType="text" - android:hint="@string/hostpref_nickname_title" - /> + android:layout_marginLeft="68dp" + android:layout_marginStart="68dp" + android:layout_marginRight="16dp" + android:layout_marginEnd="16dp" + > + + <EditText + android:id="@+id/nickname_field" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:maxLines="1" + android:inputType="text" + android:hint="@string/hostpref_nickname_title" + /> - </android.support.design.widget.TextInputLayout> + </android.support.design.widget.TextInputLayout> - <LinearLayout + </RelativeLayout> + + <RelativeLayout + android:id="@+id/color_item" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_marginStart="4dp" - android:layout_marginLeft="4dp" - android:layout_marginBottom="4dp" > + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/hostpref_color_title" + style="@style/ListItemIcon" + /> + <TextView + android:id="@+id/color_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hostpref_color_title" - android:textSize="12sp" + style="@style/ListItemFirstLineText.WithIcon" /> - <Spinner - android:id="@+id/color_selector" + <TextView + android:id="@+id/color_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:entries="@array/list_colors" - android:entryValues="@array/list_color_values" + android:layout_below="@id/color_title" + tools:text="gray" + style="@style/ListItemSecondLineText.WithIcon" /> - </LinearLayout> + </RelativeLayout> - <LinearLayout + <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_marginStart="4dp" - android:layout_marginLeft="4dp" - android:layout_marginBottom="4dp" > + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/hostpref_fontsize_title" + style="@style/ListItemIcon" + /> + + <TextView + android:id="@+id/font_size_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/hostpref_fontsize_title" + style="@style/ListItemFirstLineText.WithIcon" + /> + <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginLeft="62dp" + android:layout_marginStart="62dp" + android:paddingBottom="8dp" + android:layout_below="@id/font_size_title" > - <TextView - android:layout_width="wrap_content" + <SeekBar + android:id="@+id/font_size_bar" + android:layout_width="0dp" + android:layout_weight="1" android:layout_height="wrap_content" - android:text="@string/hostpref_fontsize_title" - android:textSize="12sp" + android:layout_gravity="center" + android:max="32" /> - <TextView + <EditText android:id="@+id/font_size_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="12sp" + android:layout_gravity="center_vertical" + android:layout_marginRight="16dp" + android:layout_marginEnd="16dp" + android:inputType="number" + android:maxLines="1" + tools:text="10" /> </LinearLayout> - <SeekBar - android:id="@+id/font_size_bar" - android:layout_width="match_parent" + </RelativeLayout> + + <RelativeLayout + android:id="@+id/pubkey_item" + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/hostpref_pubkeyid_title" + style="@style/ListItemIcon" + /> + + <TextView + android:id="@+id/pubkey_title" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:max="32" + android:text="@string/hostpref_pubkeyid_title" + style="@style/ListItemFirstLineText.WithIcon" /> - </LinearLayout> + <TextView + android:id="@+id/pubkey_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/pubkey_title" + tools:text="Use any key" + style="@style/ListItemSecondLineText.WithIcon" + /> - <View style="@style/Divider" - /> + </RelativeLayout> - <LinearLayout - android:layout_width="wrap_content" + <RelativeLayout + android:id="@+id/delkey_item" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_marginStart="4dp" - android:layout_marginLeft="4dp" - android:layout_marginBottom="4dp" > + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/hostpref_delkey_title" + style="@style/ListItemIcon" + /> + <TextView + android:id="@+id/delkey_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/hostpref_pubkeyid_title" - android:textSize="12sp" + android:text="@string/hostpref_delkey_title" + style="@style/ListItemFirstLineText.WithIcon" /> - <Spinner - android:id="@+id/pubkey_spinner" + <TextView + android:id="@+id/delkey_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:entries="@array/list_pubkeyids" - android:entryValues="@array/list_pubkeyids_value" + android:layout_below="@id/delkey_title" + tools:text="Delete" + style="@style/ListItemSecondLineText.WithIcon" /> - </LinearLayout> + </RelativeLayout> + + <RelativeLayout + android:id="@+id/encoding_item" + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/hostpref_encoding_title" + style="@style/ListItemIcon" + /> + + <TextView + android:id="@+id/encoding_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/hostpref_encoding_title" + style="@style/ListItemFirstLineText.WithIcon" + /> + + <TextView + android:id="@+id/encoding_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/encoding_title" + tools:text="UTF-8" + style="@style/ListItemSecondLineText.WithIcon" + /> + + </RelativeLayout> <LinearLayout android:layout_width="match_parent" @@ -276,14 +391,17 @@ > <RelativeLayout + android:id="@+id/use_ssh_auth_item" android:layout_width="match_parent" android:layout_height="wrap_content" > - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/hostpref_authagent_title" + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/hostpref_authagent_title" + style="@style/ListItemIcon" /> <android.support.v7.widget.SwitchCompat @@ -292,14 +410,28 @@ android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" + android:layout_marginRight="16dp" + android:layout_marginEnd="16dp" + android:layout_centerVertical="true" + /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toLeftOf="@id/use_ssh_auth_switch" + android:layout_toStartOf="@id/use_ssh_auth_switch" + android:text="@string/hostpref_authagent_title" + android:paddingBottom="24dp" + style="@style/ListItemFirstLineText.WithIcon" + tools:ignore="RelativeOverlap" /> </RelativeLayout> <RelativeLayout - android:id="@+id/ssh_confirmation_container" + android:id="@+id/ssh_auth_confirmation_item" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="32dp" android:visibility="gone" > @@ -307,6 +439,10 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/hostpref_authagent_with_confirmation" + android:textSize="14sp" + android:layout_marginTop="8dp" + android:layout_marginLeft="72dp" + android:layout_marginStart="72dp" /> <android.support.v7.widget.AppCompatCheckBox @@ -315,6 +451,7 @@ android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" + android:padding="16dp" /> </RelativeLayout> @@ -322,14 +459,25 @@ </LinearLayout> <RelativeLayout + android:id="@+id/compression_item" android:layout_width="match_parent" android:layout_height="wrap_content" > + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/hostpref_compression_title" + style="@style/ListItemIcon" + /> + <TextView - android:layout_width="match_parent" + android:id="@+id/compression_title" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hostpref_compression_title" + style="@style/ListItemFirstLineText.WithIcon" /> <android.support.v7.widget.SwitchCompat @@ -338,19 +486,43 @@ android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" + android:layout_marginRight="16dp" + android:layout_marginEnd="16dp" + android:layout_centerVertical="true" + /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toLeftOf="@id/compression_switch" + android:layout_toStartOf="@id/compression_switch" + android:layout_below="@id/compression_title" + android:text="@string/hostpref_compression_summary" + style="@style/ListItemSecondLineText.WithIcon" /> </RelativeLayout> <RelativeLayout + android:id="@+id/start_shell_item" android:layout_width="match_parent" android:layout_height="wrap_content" > + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/hostpref_wantsession_title" + style="@style/ListItemIcon" + /> + <TextView - android:layout_width="match_parent" + android:id="@+id/shell_session_title" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hostpref_wantsession_title" + style="@style/ListItemFirstLineText.WithIcon" /> <android.support.v7.widget.SwitchCompat @@ -359,19 +531,43 @@ android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" + android:layout_marginRight="16dp" + android:layout_marginEnd="16dp" + android:layout_centerVertical="true" + /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/hostpref_wantsession_summary" + android:layout_toLeftOf="@id/start_shell_switch" + android:layout_toStartOf="@id/start_shell_switch" + android:layout_below="@id/shell_session_title" + style="@style/ListItemSecondLineText.WithIcon" /> </RelativeLayout> <RelativeLayout + android:id="@+id/stay_connected_item" android:layout_width="match_parent" android:layout_height="wrap_content" > + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/hostpref_stayconnected_title" + style="@style/ListItemIcon" + /> + <TextView - android:layout_width="match_parent" + android:id="@+id/stay_connected_title" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hostpref_stayconnected_title" + style="@style/ListItemFirstLineText.WithIcon" /> <android.support.v7.widget.SwitchCompat @@ -380,19 +576,43 @@ android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" + android:layout_marginRight="16dp" + android:layout_marginEnd="16dp" + android:layout_centerVertical="true" + /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/hostpref_stayconnected_summary" + android:layout_toLeftOf="@id/stay_connected_switch" + android:layout_toStartOf="@id/stay_connected_switch" + android:layout_below="@id/stay_connected_title" + style="@style/ListItemSecondLineText.WithIcon" /> </RelativeLayout> <RelativeLayout + android:id="@+id/close_on_disconnect_item" android:layout_width="match_parent" android:layout_height="wrap_content" > + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/hostpref_quickdisconnect_title" + style="@style/ListItemIcon" + /> + <TextView - android:layout_width="match_parent" + android:id="@+id/close_on_disconnect_title" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hostpref_quickdisconnect_title" + style="@style/ListItemFirstLineText.WithIcon" /> <android.support.v7.widget.SwitchCompat @@ -401,81 +621,70 @@ android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" + android:layout_marginRight="16dp" + android:layout_marginEnd="16dp" + android:layout_centerVertical="true" /> - </RelativeLayout> - - <RelativeLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - > - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/hostpref_postlogin_title" - /> - - <EditText - android:id="@+id/post_login_automation_field" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_width="fill_parent" - android:inputType="textMultiLine" - android:lines="8" - android:minLines="2" + android:text="@string/hostpref_quickdisconnect_summary" + android:layout_toLeftOf="@id/close_on_disconnect_switch" + android:layout_toStartOf="@id/close_on_disconnect_switch" + android:layout_below="@id/close_on_disconnect_title" + style="@style/ListItemSecondLineText.WithIcon" /> </RelativeLayout> - <LinearLayout - android:layout_width="wrap_content" + <RelativeLayout + android:id="@+id/postlogin_item" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_marginStart="4dp" - android:layout_marginLeft="4dp" - android:layout_marginBottom="4dp" > - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/hostpref_delkey_title" - android:textSize="12sp" + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/icon" + android:contentDescription="@string/hostpref_postlogin_title" + style="@style/ListItemIcon" /> - <Spinner - android:id="@+id/del_key_spinner" + <TextView + android:id="@+id/postlogin_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:entries="@array/list_delkey" - android:entryValues="@array/list_delkey_values" + android:text="@string/hostpref_postlogin_title" + style="@style/ListItemFirstLineText.WithIcon" /> - </LinearLayout> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_marginStart="4dp" - android:layout_marginLeft="4dp" - android:layout_marginBottom="4dp" - > - <TextView + android:id="@+id/postlogin_summary" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/hostpref_encoding_title" - android:textSize="12sp" + android:text="@string/hostpref_postlogin_summary" + android:layout_below="@id/postlogin_title" + android:layout_alignParentBottom="false" + android:paddingBottom="2dp" + style="@style/ListItemSecondLineText.WithIcon" /> - <Spinner - android:id="@+id/encoding_spinner" - android:layout_width="wrap_content" + <EditText + android:id="@+id/post_login_automation_field" android:layout_height="wrap_content" + android:layout_width="match_parent" + android:layout_marginLeft="72dp" + android:layout_marginStart="72dp" + android:layout_below="@id/postlogin_summary" + android:inputType="textMultiLine" + android:lines="8" + android:minLines="2" /> - </LinearLayout> + + </RelativeLayout> </LinearLayout> diff --git a/app/src/main/res/values-v11/styles.xml b/app/src/main/res/values-v11/styles.xml index 79f9633..37c3f6e 100644 --- a/app/src/main/res/values-v11/styles.xml +++ b/app/src/main/res/values-v11/styles.xml @@ -25,16 +25,23 @@ </style> <style name="ListItemFirstLineText" parent="TextAppearance.AppCompat"> + <item name="android:layout_alignParentTop">true</item> <item name="android:textColor">?android:textColorPrimary</item> <item name="android:textSize">16sp</item> + <item name="android:paddingTop">20dp</item> + <item name="android:layout_alignParentLeft">true</item> </style> <style name="ListItemSecondLineText" parent="TextAppearance.AppCompat"> + <item name="android:layout_alignParentBottom">true</item> <item name="android:textColor">?android:textColorSecondary</item> <item name="android:textSize">14sp</item> + <item name="android:paddingBottom">20dp</item> + <item name="android:layout_alignParentLeft">true</item> </style> - <style name="SelectableItem"> - <item name="android:background">?android:attr/selectableItemBackground</item> - </style> + <style name="SelectableItem"> + <item name="android:background">?android:attr/selectableItemBackground</item> + </style> + </resources> diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..1698002 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ ConnectBot: simple, powerful, open-source SSH client for Android + ~ Copyright 2015 Kenny Root, Jeffrey Sharkey + ~ + ~ 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. + --> + +<resources> + <dimen name="list_item_text_margin_left">72dp</dimen> + <dimen name="list_item_left_right_padding">16dp</dimen> + <dimen name="list_item_top_bottom_padding">20dp</dimen> + <dimen name="list_item_first_line_text_size">16sp</dimen> + <dimen name="list_item_second_line_text_size">14sp</dimen> +</resources> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index af3bd79..f782fb4 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -16,7 +16,7 @@ ~ limitations under the License. */ --> -<resources> +<resources xmlns:tools="http://schemas.android.com/tools"> <style name="AppTheme" parent="Theme.AppCompat"> <item name="colorPrimary">@color/primary</item> @@ -36,14 +36,38 @@ <item name="android:background">@drawable/keyboard_button_selector</item> </style> + <style name="ListItemIcon"> + <item name="android:layout_marginLeft">@dimen/list_item_left_right_padding</item> + <item name="android:layout_marginStart" tools:ignore="NewApi">@dimen/list_item_left_right_padding</item> + <item name="android:layout_centerVertical">true</item> + <item name="android:layout_alignParentLeft">true</item> + <item name="android:layout_alignParentStart" tools:ignore="NewApi">true</item> + </style> + <style name="ListItemFirstLineText"> + <item name="android:layout_alignParentTop">true</item> <item name="android:textColor">?android:textColorPrimary</item> - <item name="android:textSize">16sp</item> + <item name="android:textSize">@dimen/list_item_first_line_text_size</item> + <item name="android:paddingTop">@dimen/list_item_top_bottom_padding</item> + <item name="android:layout_alignParentLeft">true</item> + </style> + + <style name="ListItemFirstLineText.WithIcon" parent="ListItemFirstLineText"> + <item name="android:layout_marginLeft">@dimen/list_item_text_margin_left</item> + <item name="android:layout_marginStart" tools:ignore="NewApi">@dimen/list_item_text_margin_left</item> </style> <style name="ListItemSecondLineText"> + <item name="android:layout_alignParentBottom">true</item> <item name="android:textColor">?android:textColorSecondary</item> - <item name="android:textSize">14sp</item> + <item name="android:textSize">@dimen/list_item_second_line_text_size</item> + <item name="android:paddingBottom">@dimen/list_item_top_bottom_padding</item> + <item name="android:layout_alignParentLeft">true</item> + </style> + + <style name="ListItemSecondLineText.WithIcon" parent="ListItemSecondLineText"> + <item name="android:layout_marginLeft">@dimen/list_item_text_margin_left</item> + <item name="android:layout_marginStart" tools:ignore="NewApi">@dimen/list_item_text_margin_left</item> </style> <style name="ListItemFirstLineText.Red" parent="ListItemFirstLineText"> @@ -77,10 +101,4 @@ <item name="colorAccent">@color/accent</item> </style> - <style name="Divider"> - <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">1dp</item> - <item name="android:background">?android:attr/listDivider</item> - </style> - </resources> |