diff options
Diffstat (limited to 'app')
8 files changed, 417 insertions, 26 deletions
diff --git a/app/src/main/java/org/connectbot/EditHostActivity.java b/app/src/main/java/org/connectbot/EditHostActivity.java index 6c7da11..f61924f 100644 --- a/app/src/main/java/org/connectbot/EditHostActivity.java +++ b/app/src/main/java/org/connectbot/EditHostActivity.java @@ -17,27 +17,236 @@ package org.connectbot; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.res.TypedArray; +import android.os.AsyncTask; +import android.os.IBinder; +import android.support.v4.app.FragmentManager; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import org.connectbot.bean.HostBean; +import org.connectbot.service.TerminalBridge; +import org.connectbot.service.TerminalManager; +import org.connectbot.util.HostDatabase; +import org.connectbot.util.PubkeyDatabase; public class EditHostActivity extends AppCompatActivity implements HostEditorFragment.Listener { + private static final String EXTRA_EXISTING_HOST_ID = "org.connectbot.existing_host_id"; + private static final long NO_HOST_ID = -1; + + private HostDatabase mHostDb; + private PubkeyDatabase mPubkeyDb; + private ServiceConnection mTerminalConnection; + private HostBean mHost; + private TerminalBridge mBridge; + private boolean mIsCreating; + private MenuItem mSaveHostButton; + + public static Intent createIntentForExistingHost(Context context, long existingHostId) { + Intent i = new Intent(context, EditHostActivity.class); + i.putExtra(EXTRA_EXISTING_HOST_ID, existingHostId); + return i; + } + + public static Intent createIntentForNewHost(Context context) { + return createIntentForExistingHost(context, NO_HOST_ID); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + mHostDb = HostDatabase.get(this); + mPubkeyDb = PubkeyDatabase.get(this); + + mTerminalConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + TerminalManager bound = ((TerminalManager.TerminalBinder) service).getService(); + mBridge = bound.getConnectedBridge(mHost); + } + + public void onServiceDisconnected(ComponentName name) { + mBridge = null; + } + }; + + long hostId = getIntent().getLongExtra(EXTRA_EXISTING_HOST_ID, NO_HOST_ID); + mIsCreating = hostId == NO_HOST_ID; + mHost = mIsCreating ? null : mHostDb.findHostById(hostId); + + // Note that the lists must be explicitly declared as ArrayLists because Bundle only accepts + // ArrayLists of Strings. + ArrayList<String> pubkeyNames = new ArrayList<>(); + ArrayList<String> pubkeyValues = new ArrayList<>(); + + // First, add default pubkey names and values (e.g., "use any" and "don't use any"). + TypedArray defaultPubkeyNames = getResources().obtainTypedArray(R.array.list_pubkeyids); + for (int i = 0; i < defaultPubkeyNames.length(); i++) { + pubkeyNames.add(defaultPubkeyNames.getString(i)); + } + TypedArray defaultPubkeyValues = getResources().obtainTypedArray(R.array.list_pubkeyids_value); + for (int i = 0; i < defaultPubkeyValues.length(); i++) { + pubkeyValues.add(defaultPubkeyValues.getString(i)); + } + + // Now, add pubkeys which have been added by the user. + for (CharSequence cs : mPubkeyDb.allValues(PubkeyDatabase.FIELD_PUBKEY_NICKNAME)) { + pubkeyNames.add(cs.toString()); + } + for (CharSequence cs : mPubkeyDb.allValues("_id")) { + pubkeyValues.add(cs.toString()); + } + setContentView(R.layout.activity_edit_host); + FragmentManager fm = getSupportFragmentManager(); + HostEditorFragment fragment = + (HostEditorFragment) fm.findFragmentById(R.id.fragment_container); - if (savedInstanceState == null) { - HostEditorFragment editor = HostEditorFragment.newInstance(null); + if (fragment == null) { + fragment = HostEditorFragment.newInstance(mHost, pubkeyNames, pubkeyValues); getSupportFragmentManager().beginTransaction() - .add(R.id.fragment_container, editor).commit(); + .add(R.id.fragment_container, fragment).commit(); } + + defaultPubkeyNames.recycle(); + defaultPubkeyValues.recycle(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate( + mIsCreating ? R.menu.edit_host_activity_add_menu : R.menu.edit_host_activity_edit_menu, + menu); + + mSaveHostButton = menu.getItem(0); + + // If the new host is being created, it can't be added until modifications have been made. + mSaveHostButton.setEnabled(!mIsCreating); + + return super.onCreateOptionsMenu(menu); } @Override - public void onHostUpdated(HostBean host) { + 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(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + @Override + public void onStart() { + super.onStart(); + + bindService(new Intent( + this, TerminalManager.class), mTerminalConnection, Context.BIND_AUTO_CREATE); + + final HostEditorFragment fragment = (HostEditorFragment) getSupportFragmentManager(). + findFragmentById(R.id.fragment_container); + if (CharsetHolder.isInitialized()) { + fragment.setCharsetData(CharsetHolder.getCharsetData()); + } else { + // If CharsetHolder is uninitialized, initialize it in an AsyncTask. This is necessary + // because Charset must touch the disk, which cannot be performed on the UI thread. + AsyncTask<Void, Void, Void> charsetTask = new AsyncTask<Void, Void, Void>() { + + @Override + protected Void doInBackground(Void... unused) { + CharsetHolder.initialize(); + return null; + } + + @Override + protected void onPostExecute(Void unused) { + fragment.setCharsetData(CharsetHolder.getCharsetData()); + } + }; + charsetTask.execute(); + } + } + + @Override + public void onStop() { + super.onStop(); + + unbindService(mTerminalConnection); + } + + @Override + public void onValidHostConfigured(HostBean host) { + mHost = host; + if (mSaveHostButton != null) + mSaveHostButton.setEnabled(true); + } + + @Override + public void onHostInvalidated() { + mHost = null; + if (mSaveHostButton != null) + mSaveHostButton.setEnabled(false); + } + + // Private static class used to generate a list of available Charsets. Note that this class + // must not be initialized by the UI thread because it blocks on disk access. + private static class CharsetHolder { + private static boolean mInitialized = false; + + // Map from Charset display name to Charset value (i.e., unique ID). + private static Map<String, String> mData; + + public static Map<String, String> getCharsetData() { + if (mData == null) + initialize(); + + return mData; + } + + private synchronized static void initialize() { + if (mInitialized) + return; + + mData = new HashMap<>(); + for (Map.Entry<String, Charset> entry : Charset.availableCharsets().entrySet()) { + Charset c = entry.getValue(); + if (c.canEncode() && c.isRegistered()) { + String key = entry.getKey(); + if (key.startsWith("cp")) { + // Custom CP437 charset changes. + mData.put("CP437", "CP437"); + } + mData.put(c.displayName(), entry.getKey()); + } + } + + mInitialized = true; + } + + public static boolean isInitialized() { + return mInitialized; + } } } diff --git a/app/src/main/java/org/connectbot/HostEditorFragment.java b/app/src/main/java/org/connectbot/HostEditorFragment.java index 6646b4a..56022d7 100644 --- a/app/src/main/java/org/connectbot/HostEditorFragment.java +++ b/app/src/main/java/org/connectbot/HostEditorFragment.java @@ -17,6 +17,9 @@ package org.connectbot; +import java.util.ArrayList; +import java.util.Map; + import android.content.ContentValues; import android.content.Context; import android.content.res.TypedArray; @@ -49,14 +52,22 @@ import org.connectbot.util.HostDatabase; public class HostEditorFragment extends Fragment { + private static final String ARG_EXISTING_HOST_ID = "existingHostId"; private static final String ARG_EXISTING_HOST = "existingHost"; private static final String ARG_IS_EXPANDED = "isExpanded"; + private static final String ARG_PUBKEY_NAMES = "pubkeyNames"; + private static final String ARG_PUBKEY_VALUES = "pubkeyValues"; private static final String ARG_QUICKCONNECT_STRING = "quickConnectString"; private static final int MINIMUM_FONT_SIZE = 8; // The host being edited. private HostBean mHost; + + // The pubkey lists (names and values). Note that these are declared as ArrayLists rather than + // 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). @@ -112,12 +123,16 @@ public class HostEditorFragment extends Fragment { private Spinner mDelKeySpinner; private Spinner mEncodingSpinner; - public static HostEditorFragment newInstance(HostBean existingHost) { + public static HostEditorFragment newInstance( + HostBean existingHost, ArrayList<String> pubkeyNames, ArrayList<String> pubkeyValues) { HostEditorFragment fragment = new HostEditorFragment(); Bundle args = new Bundle(); if (existingHost != null) { + args.putLong(ARG_EXISTING_HOST_ID, existingHost.getId()); args.putParcelable(ARG_EXISTING_HOST, existingHost.getValues()); } + args.putStringArrayList(ARG_PUBKEY_NAMES, pubkeyNames); + args.putStringArrayList(ARG_PUBKEY_VALUES, pubkeyValues); fragment.setArguments(args); return fragment; } @@ -135,10 +150,14 @@ public class HostEditorFragment extends Fragment { mIsCreating = existingHostParcelable == null; if (existingHostParcelable != null) { mHost = HostBean.fromContentValues((ContentValues) existingHostParcelable); + mHost.setId(bundle.getLong(ARG_EXISTING_HOST_ID)); } else { mHost = new HostBean(); } + mPubkeyNames = bundle.getStringArrayList(ARG_PUBKEY_NAMES); + mPubkeyValues = bundle.getStringArrayList(ARG_PUBKEY_VALUES); + mIsUriEditorExpanded = bundle.getBoolean(ARG_IS_EXPANDED); } @@ -154,7 +173,7 @@ public class HostEditorFragment extends Fragment { transportSelection.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mTransportSpinner.setAdapter(transportSelection); for (int i = 0; i < transportNames.length; i++) { - if (transportNames.equals(mHost.getProtocol())) { + if (transportNames[i].equals(mHost.getProtocol())) { mTransportSpinner.setSelection(i); break; } @@ -171,6 +190,7 @@ public class HostEditorFragment extends Fragment { mHost.setProtocol(protocol); mHost.setPort(TransportFactory.getTransport(protocol).getDefaultPort()); + handleHostChange(); mQuickConnectContainer.setHint( TransportFactory.getFormatHint(protocol, getActivity())); @@ -208,10 +228,12 @@ public class HostEditorFragment extends Fragment { mQuickConnectField.setText(oldQuickConnect == null ? mHost.toString() : oldQuickConnect); mQuickConnectField.addTextChangedListener(new TextWatcher() { @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } @Override - public void onTextChanged(CharSequence s, int start, int before, int count) {} + public void onTextChanged(CharSequence s, int start, int before, int count) { + } @Override public void afterTextChanged(Editable s) { @@ -265,8 +287,8 @@ public class HostEditorFragment extends Fragment { new HostTextFieldWatcher(HostDatabase.FIELD_HOST_NICKNAME)); mColorSelector = (Spinner) view.findViewById(R.id.color_selector); - for (int i = 0; i < mColorValues.getIndexCount(); i++) { - if (mHost.getColor().equals(mColorValues.getString(i))) { + for (int i = 0; i < mColorValues.length(); i++) { + if (mColorValues.getString(i).equals(mHost.getColor())) { mColorSelector.setSelection(i); break; } @@ -275,6 +297,7 @@ public class HostEditorFragment extends Fragment { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { mHost.setColor(mColorValues.getString(position)); + handleHostChange(); } @Override @@ -289,6 +312,7 @@ public class HostEditorFragment extends Fragment { public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { int fontSize = MINIMUM_FONT_SIZE + progress; mHost.setFontSize(fontSize); + handleHostChange(); mFontSizeText.setText(Integer.toString(fontSize)); } @@ -303,8 +327,30 @@ public class HostEditorFragment extends Fragment { mFontSizeSeekBar.setProgress(mHost.getFontSize() - MINIMUM_FONT_SIZE); mPubkeySpinner = (Spinner) view.findViewById(R.id.pubkey_spinner); - // TODO: Set up spinner. This requires passing pubkey data into the fragment from the - // activity and will be part of an upcoming PR. + 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); + break; + } + } + mPubkeySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + mHost.setPubkeyId(Integer.parseInt(mPubkeyValues.get(position))); + handleHostChange(); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + } + }); mUseSshConfirmationContainer = view.findViewById(R.id.ssh_confirmation_container); mUseSshAuthSwitch = (SwitchCompat) view.findViewById(R.id.use_ssh_auth_switch); @@ -323,19 +369,21 @@ public class HostEditorFragment extends Fragment { } else { mHost.setUseAuthAgent(/* don't use */ mSshAuthValues.getString(0)); } + handleHostChange(); } }; - mUseSshAuthSwitch.setOnCheckedChangeListener(authSwitchListener); - mSshAuthConfirmationCheckbox.setOnCheckedChangeListener(authSwitchListener); 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); mCompressionSwitch = (SwitchCompat) view.findViewById(R.id.compression_switch); mCompressionSwitch.setChecked(mHost.getCompression()); @@ -363,7 +411,7 @@ public class HostEditorFragment extends Fragment { new HostTextFieldWatcher(HostDatabase.FIELD_HOST_POSTLOGIN)); mDelKeySpinner = (Spinner) view.findViewById(R.id.del_key_spinner); - for (int i = 0; i < mDelKeyValues.getIndexCount(); i++) { + for (int i = 0; i < mDelKeyValues.length(); i++) { if (mHost.getDelKey().equals(mDelKeyValues.getString(i))) { mDelKeySpinner.setSelection(i); break; @@ -373,6 +421,7 @@ public class HostEditorFragment extends Fragment { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { mHost.setDelKey(mDelKeyValues.getString(position)); + handleHostChange(); } @Override @@ -381,8 +430,8 @@ public class HostEditorFragment extends Fragment { }); mEncodingSpinner = (Spinner) view.findViewById(R.id.encoding_spinner); - // TODO: Set up spinner. This requires passing pubkey data into the fragment from the - // activity and will be part of an upcoming PR. + // The spinner is initialized in setCharsetData() because Charset data is not always + // available when this fragment is created. setUriPartsContainerExpanded(mIsUriEditorExpanded); @@ -418,10 +467,48 @@ public class HostEditorFragment extends Fragment { public void onSaveInstanceState(Bundle savedInstanceState) { super.onSaveInstanceState(savedInstanceState); + savedInstanceState.putLong(ARG_EXISTING_HOST_ID, mHost.getId()); savedInstanceState.putParcelable(ARG_EXISTING_HOST, mHost.getValues()); savedInstanceState.putBoolean(ARG_IS_EXPANDED, mIsUriEditorExpanded); savedInstanceState.putString( ARG_QUICKCONNECT_STRING, mQuickConnectField.getText().toString()); + savedInstanceState.putStringArrayList(ARG_PUBKEY_NAMES, mPubkeyNames); + savedInstanceState.putStringArrayList(ARG_PUBKEY_VALUES, mPubkeyValues); + } + + /** + * Sets the Charset encoding data for the editor. + * @param data A map from Charset display name to Charset value (i.e., unique ID for the + * 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; + } + } + 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) { + } + }); + } } private void setUriPartsContainerExpanded(boolean expanded) { @@ -463,10 +550,39 @@ public class HostEditorFragment extends Fragment { mHost.setHostname(host.getHostname()); mHost.setNickname(host.getNickname()); mHost.setPort(host.getPort()); + handleHostChange(); + } + + /** + * Handles a change in the host caused by the user adjusting the values of one of the widgets + * in this fragment. If the change has resulted in a valid host, the new value is sent back + * to the listener; however, if the change ha resulted in an invalid host, the listener is + * notified. + */ + private void handleHostChange() { + String protocol = (String) mTransportSpinner.getSelectedItem(); + String quickConnectString = mQuickConnectField.getText().toString(); + if (protocol == null || protocol.equals("") || + quickConnectString == null || quickConnectString.equals("")) { + // Invalid protocol and/or string, so don't do anything. + mListener.onHostInvalidated(); + return; + } + + Uri uri = TransportFactory.getUri(protocol, quickConnectString); + if (uri == null) { + // Valid string, but does not accurately describe a URI. + mListener.onHostInvalidated(); + return; + } + + // Now, the host is confirmed to have a valid URI. + mListener.onValidHostConfigured(mHost); } public interface Listener { - public void onHostUpdated(HostBean host); + public void onValidHostConfigured(HostBean host); + public void onHostInvalidated(); } private class HostTextFieldWatcher implements TextWatcher { @@ -515,6 +631,7 @@ public class HostEditorFragment extends Fragment { mUriFieldEditInProgress = false; } } + handleHostChange(); } private boolean isUriRelatedField(String fieldType) { @@ -545,6 +662,7 @@ public class HostEditorFragment extends Fragment { } else { throw new RuntimeException("Invalid field type."); } + handleHostChange(); } } } diff --git a/app/src/main/java/org/connectbot/HostListActivity.java b/app/src/main/java/org/connectbot/HostListActivity.java index 3ad8c55..509ef80 100644 --- a/app/src/main/java/org/connectbot/HostListActivity.java +++ b/app/src/main/java/org/connectbot/HostListActivity.java @@ -223,8 +223,8 @@ public class HostListActivity extends AppCompatListActivity implements OnHostSta addHostButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - DialogFragment dialog = new AddHostDialogFragment(); - dialog.show(getSupportFragmentManager(), "AddHostDialogFragment"); + Intent intent = EditHostActivity.createIntentForNewHost(HostListActivity.this); + startActivityForResult(intent, REQUEST_EDIT); } public void onNothingSelected(AdapterView<?> arg0) {} @@ -439,8 +439,8 @@ public class HostListActivity extends AppCompatListActivity implements OnHostSta MenuItem edit = menu.add(R.string.list_host_edit); edit.setOnMenuItemClickListener(new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - Intent intent = new Intent(HostListActivity.this, HostEditorActivity.class); - intent.putExtra(Intent.EXTRA_TITLE, host.getId()); + Intent intent = EditHostActivity.createIntentForExistingHost( + HostListActivity.this, host.getId()); HostListActivity.this.startActivityForResult(intent, REQUEST_EDIT); return true; } diff --git a/app/src/main/java/org/connectbot/bean/HostBean.java b/app/src/main/java/org/connectbot/bean/HostBean.java index d6cf8f4..438713e 100644 --- a/app/src/main/java/org/connectbot/bean/HostBean.java +++ b/app/src/main/java/org/connectbot/bean/HostBean.java @@ -47,7 +47,7 @@ public class HostBean extends AbstractBean { private boolean useKeys = true; private String useAuthAgent = HostDatabase.AUTHAGENT_NO; private String postLogin = null; - private long pubkeyId = -1; + private long pubkeyId = HostDatabase.PUBKEYID_ANY; private boolean wantSession = true; private String delKey = HostDatabase.DELKEY_DEL; private int fontSize = DEFAULT_FONT_SIZE; @@ -226,8 +226,8 @@ public class HostBean extends AbstractBean { values.put(HostDatabase.FIELD_HOST_FONTSIZE, fontSize); values.put(HostDatabase.FIELD_HOST_COMPRESSION, Boolean.toString(compression)); values.put(HostDatabase.FIELD_HOST_ENCODING, encoding); - values.put(HostDatabase.FIELD_HOST_STAYCONNECTED, stayConnected); - values.put(HostDatabase.FIELD_HOST_QUICKDISCONNECT, quickDisconnect); + values.put(HostDatabase.FIELD_HOST_STAYCONNECTED, Boolean.toString(stayConnected)); + values.put(HostDatabase.FIELD_HOST_QUICKDISCONNECT, Boolean.toString(quickDisconnect)); return values; } diff --git a/app/src/main/java/org/connectbot/util/HostDatabase.java b/app/src/main/java/org/connectbot/util/HostDatabase.java index 074e081..632e333 100644 --- a/app/src/main/java/org/connectbot/util/HostDatabase.java +++ b/app/src/main/java/org/connectbot/util/HostDatabase.java @@ -200,7 +200,7 @@ public class HostDatabase extends RobustSQLiteOpenHelper implements HostStorage, + FIELD_HOST_WANTSESSION + " TEXT DEFAULT '" + Boolean.toString(true) + "', " + FIELD_HOST_COMPRESSION + " TEXT DEFAULT '" + Boolean.toString(false) + "', " + FIELD_HOST_ENCODING + " TEXT DEFAULT '" + ENCODING_DEFAULT + "', " - + FIELD_HOST_STAYCONNECTED + " TEXT, " + + FIELD_HOST_STAYCONNECTED + " TEXT DEFAULT '" + Boolean.toString(false) + "', " + FIELD_HOST_QUICKDISCONNECT + " TEXT DEFAULT '" + Boolean.toString(false) + "')"); db.execSQL("CREATE TABLE " + TABLE_PORTFORWARDS diff --git a/app/src/main/res/menu/edit_host_activity_add_menu.xml b/app/src/main/res/menu/edit_host_activity_add_menu.xml new file mode 100644 index 0000000..e793cf4 --- /dev/null +++ b/app/src/main/res/menu/edit_host_activity_add_menu.xml @@ -0,0 +1,30 @@ +<?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. + --> + +<menu + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:connectbot="http://schemas.android.com/apk/res-auto" + > + + <item android:id="@+id/save" + android:icon="@drawable/ic_add" + android:title="@string/hostpref_add_host" + connectbot:showAsAction="always|withText" + /> + +</menu> diff --git a/app/src/main/res/menu/edit_host_activity_edit_menu.xml b/app/src/main/res/menu/edit_host_activity_edit_menu.xml new file mode 100644 index 0000000..564e211 --- /dev/null +++ b/app/src/main/res/menu/edit_host_activity_edit_menu.xml @@ -0,0 +1,30 @@ +<?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. + --> + +<menu + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:connectbot="http://schemas.android.com/apk/res-auto" + > + + <item android:id="@+id/save" + android:icon="@drawable/ic_add" + android:title="@string/hostpref_edit_host" + connectbot:showAsAction="always|withText" + /> + +</menu> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index baf2db4..7d44f8d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -643,5 +643,9 @@ <string name="expand">Expand</string> <!-- Label for checkbox which, when check, makes SSL authorization require confirmation. --> <string name="hostpref_authagent_with_confirmation">require confirmation</string> + <!-- Text for button which, when clicked, adds a new host. --> + <string name="hostpref_add_host">Add host</string> + <!-- Text for button which, when clicked, saves an existing host. --> + <string name="hostpref_edit_host">Save host</string> </resources> |