1 /* //device/tools/ddms/src/com/android/ddms/PrefsDialog.java 2 ** 3 ** Copyright 2007, The Android Open Source Project 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.android.ddms; 19 20 import com.android.ddmlib.DdmConstants; 21 import com.android.ddmlib.DdmPreferences; 22 import com.android.ddmlib.Log; 23 import com.android.ddmlib.Log.LogLevel; 24 import com.android.ddmuilib.DdmUiPreferences; 25 import com.android.ddmuilib.PortFieldEditor; 26 import com.android.sdkstats.SdkStatsService; 27 28 import org.eclipse.jface.preference.BooleanFieldEditor; 29 import org.eclipse.jface.preference.DirectoryFieldEditor; 30 import org.eclipse.jface.preference.FieldEditorPreferencePage; 31 import org.eclipse.jface.preference.FontFieldEditor; 32 import org.eclipse.jface.preference.IntegerFieldEditor; 33 import org.eclipse.jface.preference.PreferenceDialog; 34 import org.eclipse.jface.preference.PreferenceManager; 35 import org.eclipse.jface.preference.PreferenceNode; 36 import org.eclipse.jface.preference.PreferencePage; 37 import org.eclipse.jface.preference.PreferenceStore; 38 import org.eclipse.jface.preference.RadioGroupFieldEditor; 39 import org.eclipse.jface.util.IPropertyChangeListener; 40 import org.eclipse.jface.util.PropertyChangeEvent; 41 import org.eclipse.swt.SWT; 42 import org.eclipse.swt.events.SelectionAdapter; 43 import org.eclipse.swt.events.SelectionEvent; 44 import org.eclipse.swt.graphics.FontData; 45 import org.eclipse.swt.graphics.Point; 46 import org.eclipse.swt.layout.GridData; 47 import org.eclipse.swt.layout.GridLayout; 48 import org.eclipse.swt.widgets.Composite; 49 import org.eclipse.swt.widgets.Control; 50 import org.eclipse.swt.widgets.Link; 51 import org.eclipse.swt.widgets.Shell; 52 53 import java.io.File; 54 import java.io.IOException; 55 56 /** 57 * Preferences dialog. 58 */ 59 public final class PrefsDialog { 60 61 // Preference store. 62 private static PreferenceStore mPrefStore; 63 64 // public const values for storage 65 public final static String SHELL_X = "shellX"; //$NON-NLS-1$ 66 public final static String SHELL_Y = "shellY"; //$NON-NLS-1$ 67 public final static String SHELL_WIDTH = "shellWidth"; //$NON-NLS-1$ 68 public final static String SHELL_HEIGHT = "shellHeight"; //$NON-NLS-1$ 69 public final static String EXPLORER_SHELL_X = "explorerShellX"; //$NON-NLS-1$ 70 public final static String EXPLORER_SHELL_Y = "explorerShellY"; //$NON-NLS-1$ 71 public final static String EXPLORER_SHELL_WIDTH = "explorerShellWidth"; //$NON-NLS-1$ 72 public final static String EXPLORER_SHELL_HEIGHT = "explorerShellHeight"; //$NON-NLS-1$ 73 public final static String SHOW_NATIVE_HEAP = "native"; //$NON-NLS-1$ 74 75 public final static String LOGCAT_COLUMN_MODE = "ddmsLogColumnMode"; //$NON-NLS-1$ 76 public final static String LOGCAT_FONT = "ddmsLogFont"; //$NON-NLS-1$ 77 78 public final static String LOGCAT_COLUMN_MODE_AUTO = "auto"; //$NON-NLS-1$ 79 public final static String LOGCAT_COLUMN_MODE_MANUAL = "manual"; //$NON-NLS-1$ 80 81 private final static String PREFS_DEBUG_PORT_BASE = "adbDebugBasePort"; //$NON-NLS-1$ 82 private final static String PREFS_SELECTED_DEBUG_PORT = "debugSelectedPort"; //$NON-NLS-1$ 83 private final static String PREFS_DEFAULT_THREAD_UPDATE = "defaultThreadUpdateEnabled"; //$NON-NLS-1$ 84 private final static String PREFS_DEFAULT_HEAP_UPDATE = "defaultHeapUpdateEnabled"; //$NON-NLS-1$ 85 private final static String PREFS_THREAD_REFRESH_INTERVAL = "threadStatusInterval"; //$NON-NLS-1$ 86 private final static String PREFS_LOG_LEVEL = "ddmsLogLevel"; //$NON-NLS-1$ 87 private final static String PREFS_TIMEOUT = "timeOut"; //$NON-NLS-1$ 88 89 90 /** 91 * Private constructor -- do not instantiate. 92 */ PrefsDialog()93 private PrefsDialog() {} 94 95 /** 96 * Return the PreferenceStore that holds our values. 97 */ getStore()98 public static PreferenceStore getStore() { 99 return mPrefStore; 100 } 101 102 /** 103 * Save the prefs to the config file. 104 */ save()105 public static void save() { 106 try { 107 mPrefStore.save(); 108 } 109 catch (IOException ioe) { 110 Log.w("ddms", "Failed saving prefs file: " + ioe.getMessage()); 111 } 112 } 113 114 /** 115 * Do some one-time prep. 116 * 117 * The original plan was to let the individual classes define their 118 * own defaults, which we would get and then override with the config 119 * file. However, PreferencesStore.load() doesn't trigger the "changed" 120 * events, which means we have to pull the loaded config values out by 121 * hand. 122 * 123 * So, we set the defaults, load the values from the config file, and 124 * then run through and manually export the values. Then we duplicate 125 * the second part later on for the "changed" events. 126 */ init()127 public static void init() { 128 assert mPrefStore == null; 129 130 mPrefStore = SdkStatsService.getPreferenceStore(); 131 132 if (mPrefStore == null) { 133 // we have a serious issue here... 134 Log.e("ddms", 135 "failed to access both the user HOME directory and the system wide temp folder. Quitting."); 136 System.exit(1); 137 } 138 139 // configure default values 140 setDefaults(System.getProperty("user.home")); //$NON-NLS-1$ 141 142 // listen for changes 143 mPrefStore.addPropertyChangeListener(new ChangeListener()); 144 145 // Now we initialize the value of the preference, from the values in the store. 146 147 // First the ddm lib. 148 DdmPreferences.setDebugPortBase(mPrefStore.getInt(PREFS_DEBUG_PORT_BASE)); 149 DdmPreferences.setSelectedDebugPort(mPrefStore.getInt(PREFS_SELECTED_DEBUG_PORT)); 150 DdmPreferences.setLogLevel(mPrefStore.getString(PREFS_LOG_LEVEL)); 151 DdmPreferences.setInitialThreadUpdate(mPrefStore.getBoolean(PREFS_DEFAULT_THREAD_UPDATE)); 152 DdmPreferences.setInitialHeapUpdate(mPrefStore.getBoolean(PREFS_DEFAULT_HEAP_UPDATE)); 153 DdmPreferences.setTimeOut(mPrefStore.getInt(PREFS_TIMEOUT)); 154 155 // some static values 156 String out = System.getenv("ANDROID_PRODUCT_OUT"); //$NON-NLS-1$ 157 DdmUiPreferences.setSymbolsLocation(out + File.separator + "symbols"); //$NON-NLS-1$ 158 DdmUiPreferences.setAddr2LineLocation("arm-eabi-addr2line"); //$NON-NLS-1$ 159 160 String traceview = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$ 161 if (traceview != null && traceview.length() != 0) { 162 traceview += File.separator + DdmConstants.FN_TRACEVIEW; 163 } else { 164 traceview = DdmConstants.FN_TRACEVIEW; 165 } 166 DdmUiPreferences.setTraceviewLocation(traceview); 167 168 // Now the ddmui lib 169 DdmUiPreferences.setStore(mPrefStore); 170 DdmUiPreferences.setThreadRefreshInterval(mPrefStore.getInt(PREFS_THREAD_REFRESH_INTERVAL)); 171 } 172 173 /* 174 * Set default values for all preferences. These are either defined 175 * statically or are based on the values set by the class initializers 176 * in other classes. 177 * 178 * The other threads (e.g. VMWatcherThread) haven't been created yet, 179 * so we want to use static values rather than reading fields from 180 * class.getInstance(). 181 */ setDefaults(String homeDir)182 private static void setDefaults(String homeDir) { 183 mPrefStore.setDefault(PREFS_DEBUG_PORT_BASE, DdmPreferences.DEFAULT_DEBUG_PORT_BASE); 184 185 mPrefStore.setDefault(PREFS_SELECTED_DEBUG_PORT, 186 DdmPreferences.DEFAULT_SELECTED_DEBUG_PORT); 187 188 mPrefStore.setDefault(PREFS_DEFAULT_THREAD_UPDATE, true); 189 mPrefStore.setDefault(PREFS_DEFAULT_HEAP_UPDATE, false); 190 mPrefStore.setDefault(PREFS_THREAD_REFRESH_INTERVAL, 191 DdmUiPreferences.DEFAULT_THREAD_REFRESH_INTERVAL); 192 193 mPrefStore.setDefault("textSaveDir", homeDir); //$NON-NLS-1$ 194 mPrefStore.setDefault("imageSaveDir", homeDir); //$NON-NLS-1$ 195 196 mPrefStore.setDefault(PREFS_LOG_LEVEL, "info"); //$NON-NLS-1$ 197 198 mPrefStore.setDefault(PREFS_TIMEOUT, DdmPreferences.DEFAULT_TIMEOUT); 199 200 // choose a default font for the text output 201 FontData fdat = new FontData("Courier", 10, SWT.NORMAL); //$NON-NLS-1$ 202 mPrefStore.setDefault("textOutputFont", fdat.toString()); //$NON-NLS-1$ 203 204 // layout information. 205 mPrefStore.setDefault(SHELL_X, 100); 206 mPrefStore.setDefault(SHELL_Y, 100); 207 mPrefStore.setDefault(SHELL_WIDTH, 800); 208 mPrefStore.setDefault(SHELL_HEIGHT, 600); 209 210 mPrefStore.setDefault(EXPLORER_SHELL_X, 50); 211 mPrefStore.setDefault(EXPLORER_SHELL_Y, 50); 212 213 mPrefStore.setDefault(SHOW_NATIVE_HEAP, false); 214 } 215 216 217 /* 218 * Create a "listener" to take action when preferences change. These are 219 * required for ongoing activities that don't check prefs on each use. 220 * 221 * This is only invoked when something explicitly changes the value of 222 * a preference (e.g. not when the prefs file is loaded). 223 */ 224 private static class ChangeListener implements IPropertyChangeListener { propertyChange(PropertyChangeEvent event)225 public void propertyChange(PropertyChangeEvent event) { 226 String changed = event.getProperty(); 227 228 if (changed.equals(PREFS_DEBUG_PORT_BASE)) { 229 DdmPreferences.setDebugPortBase(mPrefStore.getInt(PREFS_DEBUG_PORT_BASE)); 230 } else if (changed.equals(PREFS_SELECTED_DEBUG_PORT)) { 231 DdmPreferences.setSelectedDebugPort(mPrefStore.getInt(PREFS_SELECTED_DEBUG_PORT)); 232 } else if (changed.equals(PREFS_LOG_LEVEL)) { 233 DdmPreferences.setLogLevel((String)event.getNewValue()); 234 } else if (changed.equals("textSaveDir")) { 235 mPrefStore.setValue("lastTextSaveDir", 236 (String) event.getNewValue()); 237 } else if (changed.equals("imageSaveDir")) { 238 mPrefStore.setValue("lastImageSaveDir", 239 (String) event.getNewValue()); 240 } else if (changed.equals(PREFS_TIMEOUT)) { 241 DdmPreferences.setTimeOut(mPrefStore.getInt(PREFS_TIMEOUT)); 242 } else { 243 Log.v("ddms", "Preference change: " + event.getProperty() 244 + ": '" + event.getOldValue() 245 + "' --> '" + event.getNewValue() + "'"); 246 } 247 } 248 } 249 250 251 /** 252 * Create and display the dialog. 253 */ run(Shell shell)254 public static void run(Shell shell) { 255 assert mPrefStore != null; 256 257 PreferenceManager prefMgr = new PreferenceManager(); 258 259 PreferenceNode node, subNode; 260 261 // this didn't work -- got NPE, possibly from class lookup: 262 //PreferenceNode app = new PreferenceNode("app", "Application", null, 263 // AppPrefs.class.getName()); 264 265 node = new PreferenceNode("debugger", new DebuggerPrefs()); 266 prefMgr.addToRoot(node); 267 268 subNode = new PreferenceNode("panel", new PanelPrefs()); 269 //prefMgr.addTo(node.getId(), subNode); 270 prefMgr.addToRoot(subNode); 271 272 node = new PreferenceNode("LogCat", new LogCatPrefs()); 273 prefMgr.addToRoot(node); 274 275 node = new PreferenceNode("misc", new MiscPrefs()); 276 prefMgr.addToRoot(node); 277 278 node = new PreferenceNode("stats", new UsageStatsPrefs()); 279 prefMgr.addToRoot(node); 280 281 PreferenceDialog dlg = new PreferenceDialog(shell, prefMgr); 282 dlg.setPreferenceStore(mPrefStore); 283 284 // run it 285 dlg.open(); 286 287 // save prefs 288 try { 289 mPrefStore.save(); 290 } 291 catch (IOException ioe) { 292 } 293 294 // discard the stuff we created 295 //prefMgr.dispose(); 296 //dlg.dispose(); 297 } 298 299 /** 300 * "Debugger" prefs page. 301 */ 302 private static class DebuggerPrefs extends FieldEditorPreferencePage { 303 304 /** 305 * Basic constructor. 306 */ DebuggerPrefs()307 public DebuggerPrefs() { 308 super(GRID); // use "grid" layout so edit boxes line up 309 setTitle("Debugger"); 310 } 311 312 /** 313 * Create field editors. 314 */ 315 @Override createFieldEditors()316 protected void createFieldEditors() { 317 IntegerFieldEditor ife; 318 319 ife = new PortFieldEditor(PREFS_DEBUG_PORT_BASE, 320 "Starting value for local port:", getFieldEditorParent()); 321 addField(ife); 322 323 ife = new PortFieldEditor(PREFS_SELECTED_DEBUG_PORT, 324 "Port of Selected VM:", getFieldEditorParent()); 325 addField(ife); 326 } 327 } 328 329 /** 330 * "Panel" prefs page. 331 */ 332 private static class PanelPrefs extends FieldEditorPreferencePage { 333 334 /** 335 * Basic constructor. 336 */ PanelPrefs()337 public PanelPrefs() { 338 super(FLAT); // use "flat" layout 339 setTitle("Info Panels"); 340 } 341 342 /** 343 * Create field editors. 344 */ 345 @Override createFieldEditors()346 protected void createFieldEditors() { 347 BooleanFieldEditor bfe; 348 IntegerFieldEditor ife; 349 350 bfe = new BooleanFieldEditor(PREFS_DEFAULT_THREAD_UPDATE, 351 "Thread updates enabled by default", getFieldEditorParent()); 352 addField(bfe); 353 354 bfe = new BooleanFieldEditor(PREFS_DEFAULT_HEAP_UPDATE, 355 "Heap updates enabled by default", getFieldEditorParent()); 356 addField(bfe); 357 358 ife = new IntegerFieldEditor(PREFS_THREAD_REFRESH_INTERVAL, 359 "Thread status interval (seconds):", getFieldEditorParent()); 360 ife.setValidRange(1, 60); 361 addField(ife); 362 } 363 } 364 365 /** 366 * "logcat" prefs page. 367 */ 368 private static class LogCatPrefs extends FieldEditorPreferencePage { 369 370 /** 371 * Basic constructor. 372 */ LogCatPrefs()373 public LogCatPrefs() { 374 super(FLAT); // use "flat" layout 375 setTitle("Logcat"); 376 } 377 378 /** 379 * Create field editors. 380 */ 381 @Override createFieldEditors()382 protected void createFieldEditors() { 383 RadioGroupFieldEditor rgfe; 384 385 rgfe = new RadioGroupFieldEditor(PrefsDialog.LOGCAT_COLUMN_MODE, 386 "Message Column Resizing Mode", 1, new String[][] { 387 { "Manual", PrefsDialog.LOGCAT_COLUMN_MODE_MANUAL }, 388 { "Automatic", PrefsDialog.LOGCAT_COLUMN_MODE_AUTO }, 389 }, 390 getFieldEditorParent(), true); 391 addField(rgfe); 392 393 FontFieldEditor ffe = new FontFieldEditor(PrefsDialog.LOGCAT_FONT, "Text output font:", 394 getFieldEditorParent()); 395 addField(ffe); 396 } 397 } 398 399 /** 400 * "misc" prefs page. 401 */ 402 private static class MiscPrefs extends FieldEditorPreferencePage { 403 404 /** 405 * Basic constructor. 406 */ MiscPrefs()407 public MiscPrefs() { 408 super(FLAT); // use "flat" layout 409 setTitle("Misc"); 410 } 411 412 /** 413 * Create field editors. 414 */ 415 @Override createFieldEditors()416 protected void createFieldEditors() { 417 DirectoryFieldEditor dfe; 418 FontFieldEditor ffe; 419 420 IntegerFieldEditor ife = new IntegerFieldEditor(PREFS_TIMEOUT, 421 "ADB connection time out (ms):", getFieldEditorParent()); 422 addField(ife); 423 424 dfe = new DirectoryFieldEditor("textSaveDir", 425 "Default text save dir:", getFieldEditorParent()); 426 addField(dfe); 427 428 dfe = new DirectoryFieldEditor("imageSaveDir", 429 "Default image save dir:", getFieldEditorParent()); 430 addField(dfe); 431 432 ffe = new FontFieldEditor("textOutputFont", "Text output font:", 433 getFieldEditorParent()); 434 addField(ffe); 435 436 RadioGroupFieldEditor rgfe; 437 438 rgfe = new RadioGroupFieldEditor(PREFS_LOG_LEVEL, 439 "Logging Level", 1, new String[][] { 440 { "Verbose", LogLevel.VERBOSE.getStringValue() }, 441 { "Debug", LogLevel.DEBUG.getStringValue() }, 442 { "Info", LogLevel.INFO.getStringValue() }, 443 { "Warning", LogLevel.WARN.getStringValue() }, 444 { "Error", LogLevel.ERROR.getStringValue() }, 445 { "Assert", LogLevel.ASSERT.getStringValue() }, 446 }, 447 getFieldEditorParent(), true); 448 addField(rgfe); 449 } 450 } 451 452 /** 453 * "Device" prefs page. 454 */ 455 private static class UsageStatsPrefs extends PreferencePage { 456 457 private BooleanFieldEditor mOptInCheckbox; 458 private Composite mTop; 459 460 /** 461 * Basic constructor. 462 */ UsageStatsPrefs()463 public UsageStatsPrefs() { 464 setTitle("Usage Stats"); 465 } 466 467 @Override createContents(Composite parent)468 protected Control createContents(Composite parent) { 469 mTop = new Composite(parent, SWT.NONE); 470 mTop.setLayout(new GridLayout(1, false)); 471 mTop.setLayoutData(new GridData(GridData.FILL_BOTH)); 472 473 Link text = new Link(mTop, SWT.WRAP); 474 text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); 475 text.setText(SdkStatsService.BODY_TEXT); 476 477 text.addSelectionListener(new SelectionAdapter() { 478 @Override 479 public void widgetSelected(SelectionEvent event) { 480 SdkStatsService.openUrl(event.text); 481 } 482 }); 483 484 mOptInCheckbox = new BooleanFieldEditor(SdkStatsService.PING_OPT_IN, 485 SdkStatsService.CHECKBOX_TEXT, mTop); 486 mOptInCheckbox.setPage(this); 487 mOptInCheckbox.setPreferenceStore(getPreferenceStore()); 488 mOptInCheckbox.load(); 489 490 return null; 491 } 492 493 @Override doComputeSize()494 protected Point doComputeSize() { 495 if (mTop != null) { 496 return mTop.computeSize(450, SWT.DEFAULT, true); 497 } 498 499 return super.doComputeSize(); 500 } 501 502 @Override performDefaults()503 protected void performDefaults() { 504 if (mOptInCheckbox != null) { 505 mOptInCheckbox.loadDefault(); 506 } 507 super.performDefaults(); 508 } 509 510 @Override performApply()511 public void performApply() { 512 if (mOptInCheckbox != null) { 513 mOptInCheckbox.store(); 514 } 515 super.performApply(); 516 } 517 518 @Override performOk()519 public boolean performOk() { 520 if (mOptInCheckbox != null) { 521 mOptInCheckbox.store(); 522 } 523 return super.performOk(); 524 } 525 } 526 527 } 528 529 530