0
|
1 /*
|
|
2 * ConnectBot: simple, powerful, open-source SSH client for Android
|
|
3 * Copyright 2007 Kenny Root, Jeffrey Sharkey
|
|
4 *
|
|
5 * Licensed under the Apache License, Version 2.0 (the "License");
|
|
6 * you may not use this file except in compliance with the License.
|
|
7 * You may obtain a copy of the License at
|
|
8 *
|
|
9 * http://www.apache.org/licenses/LICENSE-2.0
|
|
10 *
|
|
11 * Unless required by applicable law or agreed to in writing, software
|
|
12 * distributed under the License is distributed on an "AS IS" BASIS,
|
|
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14 * See the License for the specific language governing permissions and
|
|
15 * limitations under the License.
|
|
16 */
|
|
17
|
|
18 package com.five_ten_sg.connectbot;
|
|
19
|
|
20 import java.nio.charset.Charset;
|
|
21 import java.util.Arrays;
|
|
22 import java.util.HashMap;
|
|
23 import java.util.LinkedList;
|
|
24 import java.util.List;
|
|
25 import java.util.Map;
|
|
26 import java.util.Map.Entry;
|
|
27 import java.util.Set;
|
|
28
|
|
29 import com.five_ten_sg.connectbot.bean.HostBean;
|
|
30 import com.five_ten_sg.connectbot.service.TerminalBridge;
|
|
31 import com.five_ten_sg.connectbot.service.TerminalManager;
|
|
32 import com.five_ten_sg.connectbot.util.HostDatabase;
|
|
33 import com.five_ten_sg.connectbot.util.PubkeyDatabase;
|
|
34 import android.content.ComponentName;
|
|
35 import android.content.ContentValues;
|
|
36 import android.content.Context;
|
|
37 import android.content.Intent;
|
|
38 import android.content.ServiceConnection;
|
|
39 import android.content.SharedPreferences;
|
|
40 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
|
41 import android.database.Cursor;
|
|
42 import android.database.sqlite.SQLiteDatabase;
|
|
43 import android.os.Bundle;
|
|
44 import android.os.IBinder;
|
|
45 import android.preference.CheckBoxPreference;
|
|
46 import android.preference.ListPreference;
|
|
47 import android.preference.Preference;
|
|
48 import android.preference.PreferenceActivity;
|
|
49 import android.util.Log;
|
|
50
|
|
51 public class HostEditorActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener {
|
|
52 public class CursorPreferenceHack implements SharedPreferences {
|
|
53 protected final String table;
|
|
54 protected final long id;
|
|
55
|
|
56 protected Map<String, String> values = new HashMap<String, String>();
|
|
57 // protected Map<String, String> pubkeys = new HashMap<String, String>();
|
|
58
|
|
59 public CursorPreferenceHack(String table, long id) {
|
|
60 this.table = table;
|
|
61 this.id = id;
|
|
62 cacheValues();
|
|
63 }
|
|
64
|
|
65 protected final void cacheValues() {
|
|
66 // fill a cursor and cache the values locally
|
|
67 // this makes sure we dont have any floating cursor to dispose later
|
|
68 SQLiteDatabase db = hostdb.getReadableDatabase();
|
|
69 Cursor cursor = db.query(table, null, "_id = ?",
|
|
70 new String[] { String.valueOf(id) }, null, null, null);
|
|
71
|
|
72 if (cursor.moveToFirst()) {
|
|
73 for (int i = 0; i < cursor.getColumnCount(); i++) {
|
|
74 String key = cursor.getColumnName(i);
|
|
75
|
|
76 if (key.equals(HostDatabase.FIELD_HOST_HOSTKEY)) continue;
|
|
77
|
|
78 String value = cursor.getString(i);
|
|
79 values.put(key, value);
|
|
80 }
|
|
81 }
|
|
82
|
|
83 cursor.close();
|
|
84 db.close();
|
|
85 // db = pubkeydb.getReadableDatabase();
|
|
86 // cursor = db.query(PubkeyDatabase.TABLE_PUBKEYS,
|
|
87 // new String[] { "_id", PubkeyDatabase.FIELD_PUBKEY_NICKNAME },
|
|
88 // null, null, null, null, null);
|
|
89 //
|
|
90 // if (cursor.moveToFirst()) {
|
|
91 // do {
|
|
92 // String pubkeyid = String.valueOf(cursor.getLong(0));
|
|
93 // String value = cursor.getString(1);
|
|
94 // pubkeys.put(pubkeyid, value);
|
|
95 // } while (cursor.moveToNext());
|
|
96 // }
|
|
97 //
|
|
98 // cursor.close();
|
|
99 // db.close();
|
|
100 }
|
|
101
|
|
102 public boolean contains(String key) {
|
|
103 return values.containsKey(key);
|
|
104 }
|
|
105
|
|
106 public class Editor implements SharedPreferences.Editor {
|
|
107
|
|
108 private ContentValues update = new ContentValues();
|
|
109
|
|
110 public SharedPreferences.Editor clear() {
|
|
111 Log.d(this.getClass().toString(), "clear()");
|
|
112 update = new ContentValues();
|
|
113 return this;
|
|
114 }
|
|
115
|
|
116 public boolean commit() {
|
|
117 //Log.d(this.getClass().toString(), "commit() changes back to database");
|
|
118 SQLiteDatabase db = hostdb.getWritableDatabase();
|
|
119 db.update(table, update, "_id = ?", new String[] { String.valueOf(id) });
|
|
120 db.close();
|
|
121 // make sure we refresh the parent cached values
|
|
122 cacheValues();
|
|
123
|
|
124 // and update any listeners
|
|
125 for (OnSharedPreferenceChangeListener listener : listeners) {
|
|
126 listener.onSharedPreferenceChanged(CursorPreferenceHack.this, null);
|
|
127 }
|
|
128
|
|
129 return true;
|
|
130 }
|
|
131
|
|
132 // Gingerbread compatibility
|
|
133 public void apply() {
|
|
134 commit();
|
|
135 }
|
|
136
|
|
137 public android.content.SharedPreferences.Editor putBoolean(String key, boolean value) {
|
|
138 return this.putString(key, Boolean.toString(value));
|
|
139 }
|
|
140
|
|
141 public android.content.SharedPreferences.Editor putFloat(String key, float value) {
|
|
142 return this.putString(key, Float.toString(value));
|
|
143 }
|
|
144
|
|
145 public android.content.SharedPreferences.Editor putInt(String key, int value) {
|
|
146 return this.putString(key, Integer.toString(value));
|
|
147 }
|
|
148
|
|
149 public android.content.SharedPreferences.Editor putLong(String key, long value) {
|
|
150 return this.putString(key, Long.toString(value));
|
|
151 }
|
|
152
|
|
153 public android.content.SharedPreferences.Editor putString(String key, String value) {
|
|
154 //Log.d(this.getClass().toString(), String.format("Editor.putString(key=%s, value=%s)", key, value));
|
|
155 update.put(key, value);
|
|
156 return this;
|
|
157 }
|
|
158
|
|
159 public android.content.SharedPreferences.Editor remove(String key) {
|
|
160 //Log.d(this.getClass().toString(), String.format("Editor.remove(key=%s)", key));
|
|
161 update.remove(key);
|
|
162 return this;
|
|
163 }
|
|
164
|
|
165 public android.content.SharedPreferences.Editor putStringSet(String key, Set<String> value) {
|
|
166 throw new UnsupportedOperationException("HostEditor Prefs do not support Set<String>");
|
|
167 }
|
|
168 }
|
|
169
|
|
170
|
|
171 public Editor edit() {
|
|
172 //Log.d(this.getClass().toString(), "edit()");
|
|
173 return new Editor();
|
|
174 }
|
|
175
|
|
176 public Map<String, ?> getAll() {
|
|
177 return values;
|
|
178 }
|
|
179
|
|
180 public boolean getBoolean(String key, boolean defValue) {
|
|
181 return Boolean.valueOf(this.getString(key, Boolean.toString(defValue)));
|
|
182 }
|
|
183
|
|
184 public float getFloat(String key, float defValue) {
|
|
185 return Float.valueOf(this.getString(key, Float.toString(defValue)));
|
|
186 }
|
|
187
|
|
188 public int getInt(String key, int defValue) {
|
|
189 return Integer.valueOf(this.getString(key, Integer.toString(defValue)));
|
|
190 }
|
|
191
|
|
192 public long getLong(String key, long defValue) {
|
|
193 return Long.valueOf(this.getString(key, Long.toString(defValue)));
|
|
194 }
|
|
195
|
|
196 public String getString(String key, String defValue) {
|
|
197 //Log.d(this.getClass().toString(), String.format("getString(key=%s, defValue=%s)", key, defValue));
|
|
198 if (!values.containsKey(key)) return defValue;
|
|
199
|
|
200 return values.get(key);
|
|
201 }
|
|
202
|
|
203 public Set<String> getStringSet(String key, Set<String> defValue) {
|
|
204 throw new ClassCastException("HostEditor Prefs do not support Set<String>");
|
|
205 }
|
|
206
|
|
207 protected List<OnSharedPreferenceChangeListener> listeners = new LinkedList<OnSharedPreferenceChangeListener>();
|
|
208
|
|
209 public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
|
210 listeners.add(listener);
|
|
211 }
|
|
212
|
|
213 public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
|
|
214 listeners.remove(listener);
|
|
215 }
|
|
216
|
|
217 }
|
|
218
|
|
219 @Override
|
|
220 public SharedPreferences getSharedPreferences(String name, int mode) {
|
|
221 //Log.d(this.getClass().toString(), String.format("getSharedPreferences(name=%s)", name));
|
|
222 return this.pref;
|
|
223 }
|
|
224
|
|
225 protected static final String TAG = "ConnectBot.HostEditorActivity";
|
|
226
|
|
227 protected HostDatabase hostdb = null;
|
|
228 private PubkeyDatabase pubkeydb = null;
|
|
229
|
|
230 private CursorPreferenceHack pref;
|
|
231 private ServiceConnection connection;
|
|
232
|
|
233 private HostBean host;
|
|
234 protected TerminalBridge hostBridge;
|
|
235
|
|
236 @Override
|
|
237 public void onCreate(Bundle icicle) {
|
|
238 super.onCreate(icicle);
|
|
239 long hostId = this.getIntent().getLongExtra(Intent.EXTRA_TITLE, -1);
|
|
240 // TODO: we could pass through a specific ContentProvider uri here
|
|
241 //this.getPreferenceManager().setSharedPreferencesName(uri);
|
|
242 this.hostdb = new HostDatabase(this);
|
|
243 this.pubkeydb = new PubkeyDatabase(this);
|
|
244 host = hostdb.findHostById(hostId);
|
|
245 connection = new ServiceConnection() {
|
|
246 public void onServiceConnected(ComponentName className, IBinder service) {
|
|
247 TerminalManager bound = ((TerminalManager.TerminalBinder) service).getService();
|
|
248 hostBridge = bound.getConnectedBridge(host);
|
|
249 }
|
|
250 public void onServiceDisconnected(ComponentName name) {
|
|
251 hostBridge = null;
|
|
252 }
|
|
253 };
|
|
254 this.pref = new CursorPreferenceHack(HostDatabase.TABLE_HOSTS, hostId);
|
|
255 this.pref.registerOnSharedPreferenceChangeListener(this);
|
|
256 this.addPreferencesFromResource(R.xml.host_prefs);
|
|
257 // add all existing pubkeys to our listpreference for user to choose from
|
|
258 // TODO: may be an issue here when this activity is recycled after adding a new pubkey
|
|
259 // TODO: should consider moving into onStart, but we dont have a good way of resetting the listpref after filling once
|
|
260 ListPreference pubkeyPref = (ListPreference)this.findPreference(HostDatabase.FIELD_HOST_PUBKEYID);
|
|
261 List<CharSequence> pubkeyNicks = new LinkedList<CharSequence> (Arrays.asList(pubkeyPref.getEntries()));
|
|
262 pubkeyNicks.addAll(pubkeydb.allValues(PubkeyDatabase.FIELD_PUBKEY_NICKNAME));
|
|
263 pubkeyPref.setEntries(pubkeyNicks.toArray(new CharSequence[pubkeyNicks.size()]));
|
|
264 List<CharSequence> pubkeyIds = new LinkedList<CharSequence> (Arrays.asList(pubkeyPref.getEntryValues()));
|
|
265 pubkeyIds.addAll(pubkeydb.allValues("_id"));
|
|
266 pubkeyPref.setEntryValues(pubkeyIds.toArray(new CharSequence[pubkeyIds.size()]));
|
|
267 // Populate the character set encoding list with all available
|
|
268 final ListPreference charsetPref = (ListPreference) findPreference(HostDatabase.FIELD_HOST_ENCODING);
|
|
269
|
|
270 if (CharsetHolder.isInitialized()) {
|
|
271 initCharsetPref(charsetPref);
|
|
272 }
|
|
273 else {
|
|
274 String[] currentCharsetPref = new String[1];
|
|
275 currentCharsetPref[0] = charsetPref.getValue();
|
|
276 charsetPref.setEntryValues(currentCharsetPref);
|
|
277 charsetPref.setEntries(currentCharsetPref);
|
|
278 new Thread(new Runnable() {
|
|
279 public void run() {
|
|
280 initCharsetPref(charsetPref);
|
|
281 }
|
|
282 }).start();
|
|
283 }
|
|
284
|
|
285 this.updateSummaries();
|
|
286 }
|
|
287
|
|
288 @Override
|
|
289 public void onStart() {
|
|
290 super.onStart();
|
|
291 bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE);
|
|
292
|
|
293 if (this.hostdb == null)
|
|
294 this.hostdb = new HostDatabase(this);
|
|
295
|
|
296 if (this.pubkeydb == null)
|
|
297 this.pubkeydb = new PubkeyDatabase(this);
|
|
298 }
|
|
299
|
|
300 @Override
|
|
301 public void onStop() {
|
|
302 super.onStop();
|
|
303 unbindService(connection);
|
|
304
|
|
305 if (this.hostdb != null) {
|
|
306 this.hostdb.close();
|
|
307 this.hostdb = null;
|
|
308 }
|
|
309
|
|
310 if (this.pubkeydb != null) {
|
|
311 this.pubkeydb.close();
|
|
312 this.pubkeydb = null;
|
|
313 }
|
|
314 }
|
|
315
|
|
316 private void updateSummaries() {
|
|
317 // for all text preferences, set hint as current database value
|
|
318 for (String key : this.pref.values.keySet()) {
|
|
319 if (key.equals(HostDatabase.FIELD_HOST_POSTLOGIN)) continue;
|
|
320
|
|
321 Preference pref = this.findPreference(key);
|
|
322
|
|
323 if (pref == null) continue;
|
|
324
|
|
325 if (pref instanceof CheckBoxPreference) continue;
|
|
326
|
|
327 CharSequence value = this.pref.getString(key, "");
|
|
328
|
|
329 if (key.equals(HostDatabase.FIELD_HOST_PUBKEYID)) {
|
|
330 try {
|
|
331 int pubkeyId = Integer.parseInt((String) value);
|
|
332
|
|
333 if (pubkeyId >= 0)
|
|
334 pref.setSummary(pubkeydb.getNickname(pubkeyId));
|
|
335 else if (pubkeyId == HostDatabase.PUBKEYID_ANY)
|
|
336 pref.setSummary(R.string.list_pubkeyids_any);
|
|
337 else if (pubkeyId == HostDatabase.PUBKEYID_NEVER)
|
|
338 pref.setSummary(R.string.list_pubkeyids_none);
|
|
339
|
|
340 continue;
|
|
341 }
|
|
342 catch (NumberFormatException nfe) {
|
|
343 // Fall through.
|
|
344 }
|
|
345 }
|
|
346 else if (pref instanceof ListPreference) {
|
|
347 ListPreference listPref = (ListPreference) pref;
|
|
348 int entryIndex = listPref.findIndexOfValue((String) value);
|
|
349
|
|
350 if (entryIndex >= 0)
|
|
351 value = listPref.getEntries()[entryIndex];
|
|
352 }
|
|
353
|
|
354 pref.setSummary(value);
|
|
355 }
|
|
356 }
|
|
357
|
|
358 private void initCharsetPref(final ListPreference charsetPref) {
|
|
359 charsetPref.setEntryValues(CharsetHolder.getCharsetIds());
|
|
360 charsetPref.setEntries(CharsetHolder.getCharsetNames());
|
|
361 }
|
|
362
|
|
363 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
|
364 // update values on changed preference
|
|
365 this.updateSummaries();
|
|
366
|
|
367 // Our CursorPreferenceHack always send null keys, so try to set charset anyway
|
|
368 if (hostBridge != null)
|
|
369 hostBridge.setCharset(sharedPreferences
|
|
370 .getString(HostDatabase.FIELD_HOST_ENCODING, HostDatabase.ENCODING_DEFAULT));
|
|
371 }
|
|
372
|
|
373 public static class CharsetHolder {
|
|
374 private static boolean initialized = false;
|
|
375
|
|
376 private static CharSequence[] charsetIds;
|
|
377 private static CharSequence[] charsetNames;
|
|
378
|
|
379 public static CharSequence[] getCharsetNames() {
|
|
380 if (charsetNames == null)
|
|
381 initialize();
|
|
382
|
|
383 return charsetNames;
|
|
384 }
|
|
385
|
|
386 public static CharSequence[] getCharsetIds() {
|
|
387 if (charsetIds == null)
|
|
388 initialize();
|
|
389
|
|
390 return charsetIds;
|
|
391 }
|
|
392
|
|
393 private synchronized static void initialize() {
|
|
394 if (initialized)
|
|
395 return;
|
|
396
|
|
397 List<CharSequence> charsetIdsList = new LinkedList<CharSequence>();
|
|
398 List<CharSequence> charsetNamesList = new LinkedList<CharSequence>();
|
|
399
|
|
400 for (Entry<String, Charset> entry : Charset.availableCharsets().entrySet()) {
|
|
401 Charset c = entry.getValue();
|
|
402
|
|
403 if (c.canEncode() && c.isRegistered()) {
|
|
404 String key = entry.getKey();
|
|
405
|
|
406 if (key.startsWith("cp")) {
|
|
407 // Custom CP437 charset changes
|
|
408 charsetIdsList.add("CP437");
|
|
409 charsetNamesList.add("CP437");
|
|
410 }
|
|
411
|
|
412 charsetIdsList.add(entry.getKey());
|
|
413 charsetNamesList.add(c.displayName());
|
|
414 }
|
|
415 }
|
|
416
|
|
417 charsetIds = charsetIdsList.toArray(new CharSequence[charsetIdsList.size()]);
|
|
418 charsetNames = charsetNamesList.toArray(new CharSequence[charsetNamesList.size()]);
|
|
419 initialized = true;
|
|
420 }
|
|
421
|
|
422 public static boolean isInitialized() {
|
|
423 return initialized;
|
|
424 }
|
|
425 }
|
|
426 }
|