line source
+ − /*
+ − * ConnectBot: simple, powerful, open-source SSH client for Android
+ − * Copyright 2007 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.
+ − */
+ −
+ − package com.five_ten_sg.connectbot;
+ −
+ − import java.nio.charset.Charset;
+ − import java.util.Arrays;
+ − import java.util.HashMap;
+ − import java.util.LinkedList;
+ − import java.util.List;
+ − import java.util.HashMap;
+ − import java.util.Map;
+ − import java.util.Map.Entry;
+ − import java.util.Set;
+ −
+ − import com.five_ten_sg.connectbot.bean.HostBean;
+ − import com.five_ten_sg.connectbot.service.TerminalBridge;
+ − import com.five_ten_sg.connectbot.service.TerminalManager;
+ − import com.five_ten_sg.connectbot.util.HostDatabase;
+ − import com.five_ten_sg.connectbot.util.PubkeyDatabase;
+ − import android.content.ComponentName;
+ − import android.content.ContentValues;
+ − import android.content.Context;
+ − import android.content.Intent;
+ − import android.content.ServiceConnection;
+ − import android.content.SharedPreferences;
+ − import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+ − import android.database.Cursor;
+ − import android.database.sqlite.SQLiteDatabase;
+ − import android.os.Bundle;
+ − import android.os.IBinder;
+ − import android.preference.CheckBoxPreference;
+ − import android.preference.ListPreference;
+ − import android.preference.Preference;
+ − import android.preference.PreferenceActivity;
+ − import android.util.Log;
+ −
+ − public class HostEditorActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
+ − public class CursorPreferenceHack implements SharedPreferences {
+ − protected final String table;
+ − protected final long id;
+ −
+ − protected Map<String, String> values = new HashMap<String, String>();
+ −
+ − public CursorPreferenceHack(String table, long id) {
+ − this.table = table;
+ − this.id = id;
+ − cacheValues();
+ − }
+ −
+ − protected final void cacheValues() {
+ − // fill a cursor and cache the values locally
+ − // this makes sure we dont have any floating cursor to dispose later
+ − SQLiteDatabase db = hostdb.getReadableDatabase();
+ − Cursor cursor = db.query(table, null, "_id = ?",
+ − new String[] { String.valueOf(id) }, null, null, null);
+ −
+ − if (cursor.moveToFirst()) {
+ − for (int i = 0; i < cursor.getColumnCount(); i++) {
+ − String key = cursor.getColumnName(i);
+ −
+ − if (key.equals(HostDatabase.FIELD_HOST_HOSTKEY)) continue;
+ −
+ − String value = cursor.getString(i);
+ − values.put(key, value);
+ − }
+ − }
+ −
+ − cursor.close();
+ − db.close();
+ − }
+ −
+ − public boolean contains(String key) {
+ − return values.containsKey(key);
+ − }
+ −
+ − public class Editor implements SharedPreferences.Editor {
+ −
+ − private ContentValues update = new ContentValues();
+ −
+ − public SharedPreferences.Editor clear() {
+ − Log.d(this.getClass().toString(), "clear()");
+ − update = new ContentValues();
+ − return this;
+ − }
+ −
+ − public boolean commit() {
+ − //Log.d(this.getClass().toString(), "commit() changes back to database");
+ − SQLiteDatabase db = hostdb.getWritableDatabase();
+ − db.update(table, update, "_id = ?", new String[] { String.valueOf(id) });
+ − db.close();
+ − // make sure we refresh the parent cached values
+ − cacheValues();
+ −
+ − // and update any listeners
+ − for (OnSharedPreferenceChangeListener listener : listeners) {
+ − listener.onSharedPreferenceChanged(CursorPreferenceHack.this, null);
+ − }
+ −
+ − return true;
+ − }
+ −
+ − // Gingerbread compatibility
+ − public void apply() {
+ − commit();
+ − }
+ −
+ − public android.content.SharedPreferences.Editor putBoolean(String key, boolean value) {
+ − return this.putString(key, Boolean.toString(value));
+ − }
+ −
+ − public android.content.SharedPreferences.Editor putFloat(String key, float value) {
+ − return this.putString(key, Float.toString(value));
+ − }
+ −
+ − public android.content.SharedPreferences.Editor putInt(String key, int value) {
+ − return this.putString(key, Integer.toString(value));
+ − }
+ −
+ − public android.content.SharedPreferences.Editor putLong(String key, long value) {
+ − return this.putString(key, Long.toString(value));
+ − }
+ −
+ − public android.content.SharedPreferences.Editor putString(String key, String value) {
+ − //Log.d(this.getClass().toString(), String.format("Editor.putString(key=%s, value=%s)", key, value));
+ − update.put(key, value);
+ − return this;
+ − }
+ −
+ − public android.content.SharedPreferences.Editor remove(String key) {
+ − //Log.d(this.getClass().toString(), String.format("Editor.remove(key=%s)", key));
+ − update.remove(key);
+ − return this;
+ − }
+ −
+ − public android.content.SharedPreferences.Editor putStringSet(String key, Set<String> value) {
+ − throw new UnsupportedOperationException("HostEditor Prefs do not support Set<String>");
+ − }
+ − }
+ −
+ −
+ − public Editor edit() {
+ − //Log.d(this.getClass().toString(), "edit()");
+ − return new Editor();
+ − }
+ −
+ − public Map<String, ?> getAll() {
+ − return values;
+ − }
+ −
+ − public boolean getBoolean(String key, boolean defValue) {
+ − return Boolean.valueOf(this.getString(key, Boolean.toString(defValue)));
+ − }
+ −
+ − public float getFloat(String key, float defValue) {
+ − return Float.valueOf(this.getString(key, Float.toString(defValue)));
+ − }
+ −
+ − public int getInt(String key, int defValue) {
+ − return Integer.valueOf(this.getString(key, Integer.toString(defValue)));
+ − }
+ −
+ − public long getLong(String key, long defValue) {
+ − return Long.valueOf(this.getString(key, Long.toString(defValue)));
+ − }
+ −
+ − public String getString(String key, String defValue) {
+ − //Log.d(this.getClass().toString(), String.format("getString(key=%s, defValue=%s)", key, defValue));
+ − if (!values.containsKey(key)) return defValue;
+ −
+ − return values.get(key);
+ − }
+ −
+ − public Set<String> getStringSet(String key, Set<String> defValue) {
+ − throw new ClassCastException("HostEditor Prefs do not support Set<String>");
+ − }
+ −
+ − protected List<OnSharedPreferenceChangeListener> listeners = new LinkedList<OnSharedPreferenceChangeListener>();
+ −
+ − public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+ − listeners.add(listener);
+ − }
+ −
+ − public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+ − listeners.remove(listener);
+ − }
+ −
+ − }
+ −
+ − @Override
+ − public SharedPreferences getSharedPreferences(String name, int mode) {
+ − //Log.d(this.getClass().toString(), String.format("getSharedPreferences(name=%s)", name));
+ − return this.pref;
+ − }
+ −
+ − protected static final String TAG = "ConnectBot.HostEditorActivity";
+ −
+ − protected HostDatabase hostdb = null;
+ − private PubkeyDatabase pubkeydb = null;
+ −
+ − private CursorPreferenceHack pref;
+ − private ServiceConnection connection;
+ − private Map<String, CharSequence> summaries = new HashMap<String, CharSequence>();
+ −
+ − private HostBean host;
+ − private boolean enableSSHFeatures;
+ − private boolean enable5250Features;
+ − private boolean enableAsyncFeatures;
+ −
+ − protected TerminalBridge hostBridge;
+ −
+ − @Override
+ − public void onCreate(Bundle icicle) {
+ − super.onCreate(icicle);
+ − long hostId = this.getIntent().getLongExtra(Intent.EXTRA_TITLE, -1);
+ − // TODO: we could pass through a specific ContentProvider uri here
+ − //this.getPreferenceManager().setSharedPreferencesName(uri);
+ − this.hostdb = new HostDatabase(this);
+ − this.pubkeydb = new PubkeyDatabase(this);
+ − host = hostdb.findHostById(hostId);
+ − enableSSHFeatures = host.isSSH();
+ − enable5250Features = host.is5250();
+ − enableAsyncFeatures = host.isAsync();
+ − connection = new ServiceConnection() {
+ − public void onServiceConnected(ComponentName className, IBinder service) {
+ − TerminalManager bound = ((TerminalManager.TerminalBinder) service).getService();
+ − hostBridge = bound.getConnectedBridge(host);
+ − }
+ − public void onServiceDisconnected(ComponentName name) {
+ − hostBridge = null;
+ − }
+ − };
+ − this.pref = new CursorPreferenceHack(HostDatabase.TABLE_HOSTS, hostId);
+ − this.pref.registerOnSharedPreferenceChangeListener(this);
+ − this.addPreferencesFromResource(R.xml.host_prefs);
+ − // get all the text summaries
+ − getSummaries();
+ − // disable all preferences that are not applicable to this host
+ − findPreference(HostDatabase.FIELD_HOST_PUBKEYID).setEnabled(enableSSHFeatures);
+ − findPreference(HostDatabase.FIELD_HOST_USEAUTHAGENT).setEnabled(enableSSHFeatures);
+ − findPreference(HostDatabase.FIELD_HOST_POSTLOGIN).setEnabled(!enable5250Features);
+ − findPreference(HostDatabase.FIELD_HOST_COMPRESSION).setEnabled(enableSSHFeatures);
+ − findPreference(HostDatabase.FIELD_HOST_HTTPPROXY).setEnabled(enableAsyncFeatures);
+ − findPreference(HostDatabase.FIELD_HOST_WANTSESSION).setEnabled(enableSSHFeatures);
+ − findPreference(HostDatabase.FIELD_HOST_USERNAME).setEnabled(enableSSHFeatures || enable5250Features);
+ − findPreference(HostDatabase.FIELD_HOST_EMULATION).setEnabled(!enable5250Features);
+ − findPreference(HostDatabase.CATEGORY_5250).setEnabled(enable5250Features);
+ − findPreference(HostDatabase.CATEGORY_X11).setEnabled(enableSSHFeatures);
+ − // add all existing pubkeys to our listpreference for user to choose from
+ − // TODO: may be an issue here when this activity is recycled after adding a new pubkey
+ − // TODO: should consider moving into onStart, but we dont have a good way of resetting the listpref after filling once
+ − ListPreference pubkeyPref = (ListPreference)findPreference(HostDatabase.FIELD_HOST_PUBKEYID);
+ − List<CharSequence> pubkeyNicks = new LinkedList<CharSequence> (Arrays.asList(pubkeyPref.getEntries()));
+ − pubkeyNicks.addAll(pubkeydb.allValues(PubkeyDatabase.FIELD_PUBKEY_NICKNAME));
+ − pubkeyPref.setEntries(pubkeyNicks.toArray(new CharSequence[pubkeyNicks.size()]));
+ − List<CharSequence> pubkeyIds = new LinkedList<CharSequence> (Arrays.asList(pubkeyPref.getEntryValues()));
+ − pubkeyIds.addAll(pubkeydb.allValues("_id"));
+ − pubkeyPref.setEntryValues(pubkeyIds.toArray(new CharSequence[pubkeyIds.size()]));
+ − // Populate the character set encoding list with all available
+ − final ListPreference charsetPref = (ListPreference)findPreference(HostDatabase.FIELD_HOST_ENCODING);
+ −
+ − if (CharsetHolder.isInitialized()) {
+ − initCharsetPref(charsetPref);
+ − }
+ − else {
+ − String[] currentCharsetPref = new String[1];
+ − currentCharsetPref[0] = charsetPref.getValue();
+ − charsetPref.setEntryValues(currentCharsetPref);
+ − charsetPref.setEntries(currentCharsetPref);
+ − new Thread(new Runnable() {
+ − public void run() {
+ − initCharsetPref(charsetPref);
+ − }
+ − }).start();
+ − }
+ −
+ − this.updateSummaries();
+ − }
+ −
+ − @Override
+ − public void onStart() {
+ − super.onStart();
+ − bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE);
+ −
+ − if (this.hostdb == null)
+ − this.hostdb = new HostDatabase(this);
+ −
+ − if (this.pubkeydb == null)
+ − this.pubkeydb = new PubkeyDatabase(this);
+ − }
+ −
+ − @Override
+ − public void onStop() {
+ − super.onStop();
+ − unbindService(connection);
+ −
+ − if (this.hostdb != null) {
+ − this.hostdb.close();
+ − this.hostdb = null;
+ − }
+ −
+ − if (this.pubkeydb != null) {
+ − this.pubkeydb.close();
+ − this.pubkeydb = null;
+ − }
+ − }
+ −
+ − private void getSummaries() {
+ − // get all the original text summaries
+ − for (String key : this.pref.values.keySet()) {
+ − Preference pref = this.findPreference(key);
+ − if (pref == null) continue;
+ − if (pref instanceof CheckBoxPreference) continue;
+ − CharSequence value = pref.getSummary();
+ − summaries.put(key, value);
+ − }
+ − }
+ −
+ − private void updateSummaries() {
+ − // for all text preferences, set hint as current database value if it is non-empty
+ − for (String key : this.pref.values.keySet()) {
+ − Preference pref = this.findPreference(key);
+ − if (pref == null) continue;
+ − if (pref instanceof CheckBoxPreference) continue;
+ − CharSequence value = this.pref.getString(key, "");
+ −
+ − if (key.equals(HostDatabase.FIELD_HOST_PUBKEYID)) {
+ − try {
+ − int pubkeyId = Integer.parseInt(value.toString());
+ −
+ − if (pubkeyId >= 0)
+ − pref.setSummary(pubkeydb.getNickname(pubkeyId));
+ − else if (pubkeyId == HostDatabase.PUBKEYID_ANY)
+ − pref.setSummary(R.string.list_pubkeyids_any);
+ − else if (pubkeyId == HostDatabase.PUBKEYID_NEVER)
+ − pref.setSummary(R.string.list_pubkeyids_none);
+ −
+ − continue;
+ − }
+ − catch (NumberFormatException nfe) {
+ − // Fall through.
+ − }
+ − }
+ − else if ((pref instanceof ListPreference) && (value != null)) {
+ − ListPreference listPref = (ListPreference) pref;
+ − int entryIndex = listPref.findIndexOfValue(value.toString());
+ − if (entryIndex >= 0) value = listPref.getEntries()[entryIndex];
+ − }
+ −
+ − if ((value == null) || (value.length() == 0)) value = summaries.get(key);
+ − pref.setSummary(value);
+ − }
+ − }
+ −
+ − private void initCharsetPref(final ListPreference charsetPref) {
+ − charsetPref.setEntryValues(CharsetHolder.getCharsetIds());
+ − charsetPref.setEntries(CharsetHolder.getCharsetNames());
+ − }
+ −
+ − public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ − // update values on changed preference
+ − this.updateSummaries();
+ −
+ − // Our CursorPreferenceHack always send null keys, so try to set charset anyway
+ − if (hostBridge != null)
+ − hostBridge.setCharset(sharedPreferences
+ − .getString(HostDatabase.FIELD_HOST_ENCODING, HostDatabase.ENCODING_DEFAULT));
+ − }
+ −
+ − public static class CharsetHolder {
+ − private static boolean initialized = false;
+ −
+ − private static CharSequence[] charsetIds;
+ − private static CharSequence[] charsetNames;
+ −
+ − public static CharSequence[] getCharsetNames() {
+ − if (charsetNames == null)
+ − initialize();
+ −
+ − return charsetNames;
+ − }
+ −
+ − public static CharSequence[] getCharsetIds() {
+ − if (charsetIds == null)
+ − initialize();
+ −
+ − return charsetIds;
+ − }
+ −
+ − private synchronized static void initialize() {
+ − if (initialized)
+ − return;
+ −
+ − List<CharSequence> charsetIdsList = new LinkedList<CharSequence>();
+ − List<CharSequence> charsetNamesList = new LinkedList<CharSequence>();
+ −
+ − for (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
+ − charsetIdsList.add("CP437");
+ − charsetNamesList.add("CP437");
+ − }
+ −
+ − charsetIdsList.add(entry.getKey());
+ − charsetNamesList.add(c.displayName());
+ − }
+ − }
+ −
+ − charsetIds = charsetIdsList.toArray(new CharSequence[charsetIdsList.size()]);
+ − charsetNames = charsetNamesList.toArray(new CharSequence[charsetNamesList.size()]);
+ − initialized = true;
+ − }
+ −
+ − public static boolean isInitialized() {
+ − return initialized;
+ − }
+ − }
+ − }