From 2bda23f8f7c519ca97dca8a5df20cfa6a318d3b6 Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Tue, 20 Oct 2015 11:02:38 -0700 Subject: Scroll to the previously selected view asynchronously. This avoids a race condition in ViewPager. See #301. Note that this still looks a bit janky because the pager noticably scrolls away and back to the right view. A bug has been filed with the support library team to look into a better solution. Also update the support library version. --- app/src/main/java/org/connectbot/ConsoleActivity.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'app/src/main/java/org') diff --git a/app/src/main/java/org/connectbot/ConsoleActivity.java b/app/src/main/java/org/connectbot/ConsoleActivity.java index 3aebd41..389ddcc 100644 --- a/app/src/main/java/org/connectbot/ConsoleActivity.java +++ b/app/src/main/java/org/connectbot/ConsoleActivity.java @@ -170,10 +170,15 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne // create views for all bridges on this service adapter.notifyDataSetChanged(); - int requestedIndex = bound.getBridges().indexOf(requestedBridge); + final int requestedIndex = bound.getBridges().indexOf(requestedBridge); if (requestedIndex != -1) { - setDisplayedTerminal(requestedIndex); + pager.post(new Runnable() { + @Override + public void run() { + setDisplayedTerminal(requestedIndex); + } + }); } } -- cgit v1.2.3 From aa24bfcc6d12c9bd03e50281d91613d50fc8c3c0 Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Tue, 20 Oct 2015 15:25:37 -0700 Subject: Save the last selected Uri in onSaveInstanceState. --- app/src/main/java/org/connectbot/ConsoleActivity.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'app/src/main/java/org') diff --git a/app/src/main/java/org/connectbot/ConsoleActivity.java b/app/src/main/java/org/connectbot/ConsoleActivity.java index 389ddcc..0bb7858 100644 --- a/app/src/main/java/org/connectbot/ConsoleActivity.java +++ b/app/src/main/java/org/connectbot/ConsoleActivity.java @@ -183,9 +183,9 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne } public void onServiceDisconnected(ComponentName className) { + bound = null; adapter.notifyDataSetChanged(); updateEmptyVisible(); - bound = null; } }; @@ -1040,8 +1040,8 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne // Maintain selected host if connected. if (adapter.getCurrentTerminalView() != null && !adapter.getCurrentTerminalView().bridge.isDisconnected()) { - Uri uri = adapter.getCurrentTerminalView().bridge.host.getUri(); - savedInstanceState.putString(STATE_SELECTED_URI, uri.toString()); + requested = adapter.getCurrentTerminalView().bridge.host.getUri(); + savedInstanceState.putString(STATE_SELECTED_URI, requested.toString()); } super.onSaveInstanceState(savedInstanceState); -- cgit v1.2.3 From a8fce6845cf50e9ff515b5d60fe0282b2a96da05 Mon Sep 17 00:00:00 2001 From: Kyle Horimoto Date: Tue, 20 Oct 2015 14:38:14 -0700 Subject: Add save dialog and adjust icons. --- .../main/java/org/connectbot/EditHostActivity.java | 71 ++++++++++++++++++---- 1 file changed, 60 insertions(+), 11 deletions(-) (limited to 'app/src/main/java/org') diff --git a/app/src/main/java/org/connectbot/EditHostActivity.java b/app/src/main/java/org/connectbot/EditHostActivity.java index f61924f..4b7d054 100644 --- a/app/src/main/java/org/connectbot/EditHostActivity.java +++ b/app/src/main/java/org/connectbot/EditHostActivity.java @@ -22,8 +22,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import android.app.AlertDialog; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.content.res.TypedArray; @@ -32,6 +34,7 @@ import android.os.IBinder; import android.support.v4.app.FragmentManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; +import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -46,6 +49,8 @@ public class EditHostActivity extends AppCompatActivity implements HostEditorFra private static final String EXTRA_EXISTING_HOST_ID = "org.connectbot.existing_host_id"; private static final long NO_HOST_ID = -1; + private static final int ENABLED_ALPHA = 255; + private static final int DISABLED_ALPHA = 130; private HostDatabase mHostDb; private PubkeyDatabase mPubkeyDb; @@ -135,7 +140,7 @@ public class EditHostActivity extends AppCompatActivity implements HostEditorFra mSaveHostButton = menu.getItem(0); // If the new host is being created, it can't be added until modifications have been made. - mSaveHostButton.setEnabled(!mIsCreating); + setAddSaveButtonEnabled(!mIsCreating); return super.onCreateOptionsMenu(menu); } @@ -144,14 +149,8 @@ public class EditHostActivity extends AppCompatActivity implements HostEditorFra public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.save: - mHostDb.saveHost(mHost); - - if (mBridge != null) { - // If the console is already open, apply the new encoding now. If the console - // was not yet opened, this will be applied automatically when it is opened. - mBridge.setCharset(mHost.getEncoding()); - } - finish(); + case android.R.id.home: + attemptSaveAndExit(); return true; default: return super.onOptionsItemSelected(item); @@ -200,14 +199,64 @@ public class EditHostActivity extends AppCompatActivity implements HostEditorFra public void onValidHostConfigured(HostBean host) { mHost = host; if (mSaveHostButton != null) - mSaveHostButton.setEnabled(true); + setAddSaveButtonEnabled(true); } @Override public void onHostInvalidated() { mHost = null; if (mSaveHostButton != null) - mSaveHostButton.setEnabled(false); + setAddSaveButtonEnabled(false); + } + + @Override + public void onBackPressed() { + attemptSaveAndExit(); + } + + /** + * If the host represents a valid URI, save it and exit; otherwise, pop up a dialog asking + * the user if he/she wants to discard the changes. + */ + private void attemptSaveAndExit() { + if (mHost == null) { + showDiscardDialog(); + return; + } + + mHostDb.saveHost(mHost); + + if (mBridge != null) { + // If the console is already open, apply the new encoding now. If the console + // was not yet opened, this will be applied automatically when it is opened. + mBridge.setCharset(mHost.getEncoding()); + } + finish(); + } + + private void showDiscardDialog() { + android.support.v7.app.AlertDialog.Builder builder = + new android.support.v7.app.AlertDialog.Builder(this); + builder.setMessage(R.string.discard_host_changes_message) + .setPositiveButton(R.string.discard_host_button, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Do not save to the database - just exit. + finish(); + } + }) + .setNegativeButton(R.string.discard_host_cancel_button, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Do nothing. + } + }); + builder.show(); + } + + private void setAddSaveButtonEnabled(boolean enabled) { + mSaveHostButton.setEnabled(enabled); + mSaveHostButton.getIcon().setAlpha(enabled ? ENABLED_ALPHA : DISABLED_ALPHA); } // Private static class used to generate a list of available Charsets. Note that this class -- cgit v1.2.3 From 766ed2875d76fc9a7979fc69616b49a116a6cea6 Mon Sep 17 00:00:00 2001 From: Kyle Horimoto Date: Wed, 21 Oct 2015 12:01:44 -0700 Subject: Lint. --- app/src/main/java/org/connectbot/EditHostActivity.java | 2 -- 1 file changed, 2 deletions(-) (limited to 'app/src/main/java/org') diff --git a/app/src/main/java/org/connectbot/EditHostActivity.java b/app/src/main/java/org/connectbot/EditHostActivity.java index 4b7d054..d5e2458 100644 --- a/app/src/main/java/org/connectbot/EditHostActivity.java +++ b/app/src/main/java/org/connectbot/EditHostActivity.java @@ -22,7 +22,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Map; -import android.app.AlertDialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; @@ -34,7 +33,6 @@ import android.os.IBinder; import android.support.v4.app.FragmentManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; -import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -- cgit v1.2.3 From d57ab6f788707defad29df2432eafe1bc4dd4e79 Mon Sep 17 00:00:00 2001 From: Kyle Horimoto Date: Tue, 20 Oct 2015 11:52:14 -0700 Subject: Polish UI for add/edit host. --- .../java/org/connectbot/HostEditorFragment.java | 581 +++++++++++++-------- .../main/java/org/connectbot/bean/HostBean.java | 2 +- 2 files changed, 367 insertions(+), 216 deletions(-) (limited to 'app/src/main/java/org') diff --git a/app/src/main/java/org/connectbot/HostEditorFragment.java b/app/src/main/java/org/connectbot/HostEditorFragment.java index 56022d7..2a6514e 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,10 +30,12 @@ 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; @@ -59,7 +62,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 +74,6 @@ public class HostEditorFragment extends Fragment { // Lists because Bundles can only contain ArrayLists, not general Lists. private ArrayList mPubkeyNames; private ArrayList 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 +88,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 mCharsetData; + + private View mTransportItem; + private TextView mTransportText; private TextInputLayout mQuickConnectContainer; private EditText mQuickConnectField; private ImageButton mExpandCollapseButton; @@ -108,20 +117,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 pubkeyNames, ArrayList pubkeyValues) { @@ -147,7 +165,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 +183,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 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 +223,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 +260,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 +329,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 pubkeySelection = new ArrayAdapter( - 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 +628,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 +640,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 +667,17 @@ public class HostEditorFragment extends Fragment { * Charset). */ public void setCharsetData(final Map data) { - if (mEncodingSpinner != null) { - final String[] encodingNames = new String[data.keySet().size()]; - data.keySet().toArray(encodingNames); - ArrayAdapter encodingSelection = new ArrayAdapter( - 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 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 +730,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 +749,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 +785,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 +816,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; -- cgit v1.2.3 From 4caf205c1abfd17dc4c605ec579df23df8f72d0e Mon Sep 17 00:00:00 2001 From: Kyle Horimoto Date: Tue, 20 Oct 2015 18:18:47 -0700 Subject: Lint and jlklein comments. --- app/src/main/java/org/connectbot/HostEditorFragment.java | 3 --- 1 file changed, 3 deletions(-) (limited to 'app/src/main/java/org') diff --git a/app/src/main/java/org/connectbot/HostEditorFragment.java b/app/src/main/java/org/connectbot/HostEditorFragment.java index 2a6514e..fe6f838 100644 --- a/app/src/main/java/org/connectbot/HostEditorFragment.java +++ b/app/src/main/java/org/connectbot/HostEditorFragment.java @@ -38,13 +38,10 @@ 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; -- cgit v1.2.3