From: Raphael Date: Thu, 22 Sep 2011 22:27:35 +0000 (-0700) Subject: Extract DDMS PreferenceStore. Do not merge. X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=241bf5cff65d30fe2224d8931e1d05d3b3a4a785;p=android-x86%2Fsdk.git Extract DDMS PreferenceStore. Do not merge. Splits DDMS PreferenceStore management in a separate class and add the methods we need for ADT first-time wizard. Various pieces of DDMS access the pref store via the PrefsDialog. This could use a cleanup eventually later. (cherry picked from commit f7bc23a0f19d2e3371cbea41d4cf8a1081c9d2d8) Change-Id: Iac37f970e69b7d74d9dd15120b838bcc47ba1caa --- diff --git a/ddms/app/src/com/android/ddms/Main.java b/ddms/app/src/com/android/ddms/Main.java index b538ba95e..14ef910c9 100644 --- a/ddms/app/src/com/android/ddms/Main.java +++ b/ddms/app/src/com/android/ddms/Main.java @@ -76,14 +76,13 @@ public class Main { // if this is the first time using ddms or adt, open up the stats service // opt out dialog, and request user for permissions. - if (!SdkStatsService.pingPermissionsSet()) { - SdkStatsService.getUserPermissionForPing(new Shell(Display.getDefault())); - } + SdkStatsService stats = new SdkStatsService(); + stats.checkUserPermissionForPing(new Shell(Display.getDefault())); // the "ping" argument means to check in with the server and exit // the application name and version number must also be supplied if (args.length >= 3 && args[0].equals("ping")) { - SdkStatsService.ping(args[1], args[2]); + stats.ping(args[1], args[2]); return; } else if (args.length > 0) { Log.e("ddms", "Unknown argument: " + args[0]); @@ -101,7 +100,8 @@ public class Main { // we're past the point where ddms can be called just to send a ping, so we can // ping for ddms itself. - ping(ddmsParentLocation); + ping(stats, ddmsParentLocation); + stats = null; DebugPortManager.setProvider(DebugPortProvider.getInstance()); @@ -131,7 +131,7 @@ public class Main { return System.getProperty("os.name").startsWith("Mac OS"); //$NON-NLS-1$ //$NON-NLS-2$ } - private static void ping(String ddmsParentLocation) { + private static void ping(SdkStatsService stats, String ddmsParentLocation) { Properties p = new Properties(); try{ File sourceProp; @@ -143,7 +143,7 @@ public class Main { p.load(new FileInputStream(sourceProp)); sRevision = p.getProperty("Pkg.Revision"); //$NON-NLS-1$ if (sRevision != null && sRevision.length() > 0) { - SdkStatsService.ping("ddms", sRevision); //$NON-NLS-1$ + stats.ping("ddms", sRevision); //$NON-NLS-1$ } } catch (FileNotFoundException e) { // couldn't find the file? don't ping. diff --git a/ddms/app/src/com/android/ddms/PrefsDialog.java b/ddms/app/src/com/android/ddms/PrefsDialog.java index 4745ed7ea..0669d7ce4 100644 --- a/ddms/app/src/com/android/ddms/PrefsDialog.java +++ b/ddms/app/src/com/android/ddms/PrefsDialog.java @@ -1,19 +1,18 @@ -/* //device/tools/ddms/src/com/android/ddms/PrefsDialog.java -** -** Copyright 2007, The Android Open Source Project -** -** 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. -*/ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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.android.ddms; @@ -25,8 +24,8 @@ import com.android.ddmuilib.DdmUiPreferences; import com.android.ddmuilib.PortFieldEditor; import com.android.ddmuilib.logcat.LogCatMessageList; import com.android.ddmuilib.logcat.LogCatPanel; +import com.android.sdkstats.DdmsPreferenceStore; import com.android.sdkstats.SdkStatsPermissionDialog; -import com.android.sdkstats.SdkStatsService; import org.eclipse.jface.preference.BooleanFieldEditor; import org.eclipse.jface.preference.DirectoryFieldEditor; @@ -63,9 +62,6 @@ import java.io.IOException; */ public final class PrefsDialog { - // Preference store. - private static PreferenceStore mPrefStore; - // public const values for storage public final static String SHELL_X = "shellX"; //$NON-NLS-1$ public final static String SHELL_Y = "shellY"; //$NON-NLS-1$ @@ -94,6 +90,8 @@ public final class PrefsDialog { private final static String PREFS_USE_ADBHOST = "useAdbHost"; //$NON-NLS-1$ private final static String PREFS_ADBHOST_VALUE = "adbHostValue"; //$NON-NLS-1$ + // Preference store. + private static DdmsPreferenceStore mStore = new DdmsPreferenceStore(); /** * Private constructor -- do not instantiate. @@ -102,17 +100,23 @@ public final class PrefsDialog { /** * Return the PreferenceStore that holds our values. + * + * @deprecated Callers should use {@link DdmsPreferenceStore} directly. */ + @Deprecated public static PreferenceStore getStore() { - return mPrefStore; + return mStore.getPreferenceStore(); } /** * Save the prefs to the config file. + * + * @deprecated Callers should use {@link DdmsPreferenceStore} directly. */ + @Deprecated public static void save() { try { - mPrefStore.save(); + mStore.getPreferenceStore().save(); } catch (IOException ioe) { Log.w("ddms", "Failed saving prefs file: " + ioe.getMessage()); @@ -133,11 +137,9 @@ public final class PrefsDialog { * the second part later on for the "changed" events. */ public static void init() { - assert mPrefStore == null; + PreferenceStore prefStore = mStore.getPreferenceStore(); - mPrefStore = SdkStatsService.getPreferenceStore(); - - if (mPrefStore == null) { + if (prefStore == null) { // we have a serious issue here... Log.e("ddms", "failed to access both the user HOME directory and the system wide temp folder. Quitting."); @@ -148,20 +150,20 @@ public final class PrefsDialog { setDefaults(System.getProperty("user.home")); //$NON-NLS-1$ // listen for changes - mPrefStore.addPropertyChangeListener(new ChangeListener()); + prefStore.addPropertyChangeListener(new ChangeListener()); // Now we initialize the value of the preference, from the values in the store. // First the ddm lib. - DdmPreferences.setDebugPortBase(mPrefStore.getInt(PREFS_DEBUG_PORT_BASE)); - DdmPreferences.setSelectedDebugPort(mPrefStore.getInt(PREFS_SELECTED_DEBUG_PORT)); - DdmPreferences.setLogLevel(mPrefStore.getString(PREFS_LOG_LEVEL)); - DdmPreferences.setInitialThreadUpdate(mPrefStore.getBoolean(PREFS_DEFAULT_THREAD_UPDATE)); - DdmPreferences.setInitialHeapUpdate(mPrefStore.getBoolean(PREFS_DEFAULT_HEAP_UPDATE)); - DdmPreferences.setTimeOut(mPrefStore.getInt(PREFS_TIMEOUT)); - DdmPreferences.setProfilerBufferSizeMb(mPrefStore.getInt(PREFS_PROFILER_BUFFER_SIZE_MB)); - DdmPreferences.setUseAdbHost(mPrefStore.getBoolean(PREFS_USE_ADBHOST)); - DdmPreferences.setAdbHostValue(mPrefStore.getString(PREFS_ADBHOST_VALUE)); + DdmPreferences.setDebugPortBase(prefStore.getInt(PREFS_DEBUG_PORT_BASE)); + DdmPreferences.setSelectedDebugPort(prefStore.getInt(PREFS_SELECTED_DEBUG_PORT)); + DdmPreferences.setLogLevel(prefStore.getString(PREFS_LOG_LEVEL)); + DdmPreferences.setInitialThreadUpdate(prefStore.getBoolean(PREFS_DEFAULT_THREAD_UPDATE)); + DdmPreferences.setInitialHeapUpdate(prefStore.getBoolean(PREFS_DEFAULT_HEAP_UPDATE)); + DdmPreferences.setTimeOut(prefStore.getInt(PREFS_TIMEOUT)); + DdmPreferences.setProfilerBufferSizeMb(prefStore.getInt(PREFS_PROFILER_BUFFER_SIZE_MB)); + DdmPreferences.setUseAdbHost(prefStore.getBoolean(PREFS_USE_ADBHOST)); + DdmPreferences.setAdbHostValue(prefStore.getString(PREFS_ADBHOST_VALUE)); // some static values String out = System.getenv("ANDROID_PRODUCT_OUT"); //$NON-NLS-1$ @@ -177,8 +179,8 @@ public final class PrefsDialog { DdmUiPreferences.setTraceviewLocation(traceview); // Now the ddmui lib - DdmUiPreferences.setStore(mPrefStore); - DdmUiPreferences.setThreadRefreshInterval(mPrefStore.getInt(PREFS_THREAD_REFRESH_INTERVAL)); + DdmUiPreferences.setStore(prefStore); + DdmUiPreferences.setThreadRefreshInterval(prefStore.getInt(PREFS_THREAD_REFRESH_INTERVAL)); } /* @@ -191,42 +193,44 @@ public final class PrefsDialog { * class.getInstance(). */ private static void setDefaults(String homeDir) { - mPrefStore.setDefault(PREFS_DEBUG_PORT_BASE, DdmPreferences.DEFAULT_DEBUG_PORT_BASE); + PreferenceStore prefStore = mStore.getPreferenceStore(); + + prefStore.setDefault(PREFS_DEBUG_PORT_BASE, DdmPreferences.DEFAULT_DEBUG_PORT_BASE); - mPrefStore.setDefault(PREFS_SELECTED_DEBUG_PORT, + prefStore.setDefault(PREFS_SELECTED_DEBUG_PORT, DdmPreferences.DEFAULT_SELECTED_DEBUG_PORT); - mPrefStore.setDefault(PREFS_USE_ADBHOST, DdmPreferences.DEFAULT_USE_ADBHOST); - mPrefStore.setDefault(PREFS_ADBHOST_VALUE, DdmPreferences.DEFAULT_ADBHOST_VALUE); + prefStore.setDefault(PREFS_USE_ADBHOST, DdmPreferences.DEFAULT_USE_ADBHOST); + prefStore.setDefault(PREFS_ADBHOST_VALUE, DdmPreferences.DEFAULT_ADBHOST_VALUE); - mPrefStore.setDefault(PREFS_DEFAULT_THREAD_UPDATE, true); - mPrefStore.setDefault(PREFS_DEFAULT_HEAP_UPDATE, false); - mPrefStore.setDefault(PREFS_THREAD_REFRESH_INTERVAL, + prefStore.setDefault(PREFS_DEFAULT_THREAD_UPDATE, true); + prefStore.setDefault(PREFS_DEFAULT_HEAP_UPDATE, false); + prefStore.setDefault(PREFS_THREAD_REFRESH_INTERVAL, DdmUiPreferences.DEFAULT_THREAD_REFRESH_INTERVAL); - mPrefStore.setDefault("textSaveDir", homeDir); //$NON-NLS-1$ - mPrefStore.setDefault("imageSaveDir", homeDir); //$NON-NLS-1$ + prefStore.setDefault("textSaveDir", homeDir); //$NON-NLS-1$ + prefStore.setDefault("imageSaveDir", homeDir); //$NON-NLS-1$ - mPrefStore.setDefault(PREFS_LOG_LEVEL, "info"); //$NON-NLS-1$ + prefStore.setDefault(PREFS_LOG_LEVEL, "info"); //$NON-NLS-1$ - mPrefStore.setDefault(PREFS_TIMEOUT, DdmPreferences.DEFAULT_TIMEOUT); - mPrefStore.setDefault(PREFS_PROFILER_BUFFER_SIZE_MB, + prefStore.setDefault(PREFS_TIMEOUT, DdmPreferences.DEFAULT_TIMEOUT); + prefStore.setDefault(PREFS_PROFILER_BUFFER_SIZE_MB, DdmPreferences.DEFAULT_PROFILER_BUFFER_SIZE_MB); // choose a default font for the text output FontData fdat = new FontData("Courier", 10, SWT.NORMAL); //$NON-NLS-1$ - mPrefStore.setDefault("textOutputFont", fdat.toString()); //$NON-NLS-1$ + prefStore.setDefault("textOutputFont", fdat.toString()); //$NON-NLS-1$ // layout information. - mPrefStore.setDefault(SHELL_X, 100); - mPrefStore.setDefault(SHELL_Y, 100); - mPrefStore.setDefault(SHELL_WIDTH, 800); - mPrefStore.setDefault(SHELL_HEIGHT, 600); + prefStore.setDefault(SHELL_X, 100); + prefStore.setDefault(SHELL_Y, 100); + prefStore.setDefault(SHELL_WIDTH, 800); + prefStore.setDefault(SHELL_HEIGHT, 600); - mPrefStore.setDefault(EXPLORER_SHELL_X, 50); - mPrefStore.setDefault(EXPLORER_SHELL_Y, 50); + prefStore.setDefault(EXPLORER_SHELL_X, 50); + prefStore.setDefault(EXPLORER_SHELL_Y, 50); - mPrefStore.setDefault(SHOW_NATIVE_HEAP, false); + prefStore.setDefault(SHOW_NATIVE_HEAP, false); } @@ -240,28 +244,29 @@ public final class PrefsDialog { private static class ChangeListener implements IPropertyChangeListener { public void propertyChange(PropertyChangeEvent event) { String changed = event.getProperty(); + PreferenceStore prefStore = mStore.getPreferenceStore(); if (changed.equals(PREFS_DEBUG_PORT_BASE)) { - DdmPreferences.setDebugPortBase(mPrefStore.getInt(PREFS_DEBUG_PORT_BASE)); + DdmPreferences.setDebugPortBase(prefStore.getInt(PREFS_DEBUG_PORT_BASE)); } else if (changed.equals(PREFS_SELECTED_DEBUG_PORT)) { - DdmPreferences.setSelectedDebugPort(mPrefStore.getInt(PREFS_SELECTED_DEBUG_PORT)); + DdmPreferences.setSelectedDebugPort(prefStore.getInt(PREFS_SELECTED_DEBUG_PORT)); } else if (changed.equals(PREFS_LOG_LEVEL)) { DdmPreferences.setLogLevel((String)event.getNewValue()); } else if (changed.equals("textSaveDir")) { - mPrefStore.setValue("lastTextSaveDir", + prefStore.setValue("lastTextSaveDir", (String) event.getNewValue()); } else if (changed.equals("imageSaveDir")) { - mPrefStore.setValue("lastImageSaveDir", + prefStore.setValue("lastImageSaveDir", (String) event.getNewValue()); } else if (changed.equals(PREFS_TIMEOUT)) { - DdmPreferences.setTimeOut(mPrefStore.getInt(PREFS_TIMEOUT)); + DdmPreferences.setTimeOut(prefStore.getInt(PREFS_TIMEOUT)); } else if (changed.equals(PREFS_PROFILER_BUFFER_SIZE_MB)) { DdmPreferences.setProfilerBufferSizeMb( - mPrefStore.getInt(PREFS_PROFILER_BUFFER_SIZE_MB)); + prefStore.getInt(PREFS_PROFILER_BUFFER_SIZE_MB)); } else if (changed.equals(PREFS_USE_ADBHOST)) { - DdmPreferences.setUseAdbHost(mPrefStore.getBoolean(PREFS_USE_ADBHOST)); + DdmPreferences.setUseAdbHost(prefStore.getBoolean(PREFS_USE_ADBHOST)); } else if (changed.equals(PREFS_ADBHOST_VALUE)) { - DdmPreferences.setAdbHostValue(mPrefStore.getString(PREFS_ADBHOST_VALUE)); + DdmPreferences.setAdbHostValue(prefStore.getString(PREFS_ADBHOST_VALUE)); } else { Log.v("ddms", "Preference change: " + event.getProperty() + ": '" + event.getOldValue() @@ -275,7 +280,8 @@ public final class PrefsDialog { * Create and display the dialog. */ public static void run(Shell shell) { - assert mPrefStore != null; + PreferenceStore prefStore = mStore.getPreferenceStore(); + assert prefStore != null; PreferenceManager prefMgr = new PreferenceManager(); @@ -302,7 +308,7 @@ public final class PrefsDialog { prefMgr.addToRoot(node); PreferenceDialog dlg = new PreferenceDialog(shell, prefMgr); - dlg.setPreferenceStore(mPrefStore); + dlg.setPreferenceStore(prefStore); // run it try { @@ -313,7 +319,7 @@ public final class PrefsDialog { // save prefs try { - mPrefStore.save(); + prefStore.save(); } catch (IOException ioe) { } @@ -549,7 +555,7 @@ public final class PrefsDialog { } }); - mOptInCheckbox = new BooleanFieldEditor(SdkStatsService.PING_OPT_IN, + mOptInCheckbox = new BooleanFieldEditor(DdmsPreferenceStore.PING_OPT_IN, SdkStatsPermissionDialog.CHECKBOX_TEXT, mTop); mOptInCheckbox.setPage(this); mOptInCheckbox.setPreferenceStore(getPreferenceStore()); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java index 7fb22940d..c67afdbb5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java @@ -232,9 +232,8 @@ public class AndroidPreferencePage extends FieldEditorPreferencePage implements /* When the ADT preferences page is made visible, display the dialog to obtain * permissions for the ping service. */ - if (!SdkStatsService.pingPermissionsSet()) { - Shell parent = getShell(); - SdkStatsService.getUserPermissionForPing(parent); - } + SdkStatsService stats = new SdkStatsService(); + Shell parent = getShell(); + stats.checkUserPermissionForPing(parent); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/UsagePreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/UsagePreferencePage.java index 6c07fe937..a333bc5f9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/UsagePreferencePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/UsagePreferencePage.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.adt.internal.preferences; +import com.android.sdkstats.DdmsPreferenceStore; import com.android.sdkstats.SdkStatsPermissionDialog; import com.android.sdkstats.SdkStatsService; @@ -40,6 +41,7 @@ public class UsagePreferencePage extends PreferencePage implements IWorkbenchPre private static final int WRAP_WIDTH_PX = 200; private BooleanFieldEditor mOptInCheckBox; + private DdmsPreferenceStore mStore = new DdmsPreferenceStore(); public UsagePreferencePage() { } @@ -73,10 +75,10 @@ public class UsagePreferencePage extends PreferencePage implements IWorkbenchPre } }); - mOptInCheckBox = new BooleanFieldEditor(SdkStatsService.PING_OPT_IN, + mOptInCheckBox = new BooleanFieldEditor(DdmsPreferenceStore.PING_OPT_IN, SdkStatsPermissionDialog.CHECKBOX_TEXT, top); mOptInCheckBox.setPage(this); - mOptInCheckBox.setPreferenceStore(SdkStatsService.getPreferenceStore()); + mOptInCheckBox.setPreferenceStore(mStore.getPreferenceStore()); mOptInCheckBox.load(); return top; @@ -119,14 +121,6 @@ public class UsagePreferencePage extends PreferencePage implements IWorkbenchPre } private void save() { - try { - PreferenceStore store = SdkStatsService.getPreferenceStore(); - if (store != null) { - store.setValue(SdkStatsService.PING_OPT_IN, mOptInCheckBox.getBooleanValue()); - store.save(); - } - } catch (IOException e) { - e.printStackTrace(); - } + mStore.setPingOptIn(mOptInCheckBox.getBooleanValue()); } } diff --git a/sdkstats/src/com/android/sdkstats/DdmsPreferenceStore.java b/sdkstats/src/com/android/sdkstats/DdmsPreferenceStore.java new file mode 100755 index 000000000..8875eb67c --- /dev/null +++ b/sdkstats/src/com/android/sdkstats/DdmsPreferenceStore.java @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * 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.android.sdkstats; + +import com.android.prefs.AndroidLocation; +import com.android.prefs.AndroidLocation.AndroidLocationException; + +import org.eclipse.jface.preference.PreferenceStore; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Random; + +/** Utility class to send "ping" usage reports to the server. */ +public class DdmsPreferenceStore { + + public final static String PING_OPT_IN = "pingOptIn"; //$NON-NLS-1$ + private final static String PING_TIME = "pingTime"; //$NON-NLS-1$ + private final static String PING_ID = "pingId"; //$NON-NLS-1$ + private final static String ADT_STARTUP_WIZARD = "adtStrtWzd"; //$NON-NLS-1$ + private final static String LAST_SDK_PATH = "lastSdkPath"; //$NON-NLS-1$ + + /** + * Lock for the preference store. + */ + private static volatile Object[] sLock = new Object[0]; + /** + * PreferenceStore for DDMS. Creation and usage must be synchronized on sLock. + * Don't use it directly, instead retrieve it via {@link #getPreferenceStore()}. + */ + private static volatile PreferenceStore sPrefStore; + + public DdmsPreferenceStore() { + } + + /** + * Returns the DDMS {@link PreferenceStore}. + * This keeps a static reference on the store, so consequent calls will + * return always the same store. + */ + public PreferenceStore getPreferenceStore() { + synchronized(sLock) { + if (sPrefStore == null) { + // get the location of the preferences + String homeDir = null; + try { + homeDir = AndroidLocation.getFolder(); + } catch (AndroidLocationException e1) { + // pass, we'll do a dummy store since homeDir is null + } + + if (homeDir == null) { + sPrefStore = new PreferenceStore(); + return sPrefStore; + } + + assert homeDir != null; + + String rcFileName = homeDir + "ddms.cfg"; //$NON-NLS-1$ + + // also look for an old pref file in the previous location + String oldPrefPath = System.getProperty("user.home") //$NON-NLS-1$ + + File.separator + ".ddmsrc"; //$NON-NLS-1$ + File oldPrefFile = new File(oldPrefPath); + if (oldPrefFile.isFile()) { + try { + PreferenceStore oldStore = new PreferenceStore(oldPrefPath); + oldStore.load(); + + oldStore.save(new FileOutputStream(rcFileName), ""); //$NON-NLS-1$ + oldPrefFile.delete(); + + PreferenceStore newStore = new PreferenceStore(rcFileName); + newStore.load(); + sPrefStore = newStore; + } catch (IOException e) { + // create a new empty store. + sPrefStore = new PreferenceStore(rcFileName); + } + } else { + sPrefStore = new PreferenceStore(rcFileName); + + try { + sPrefStore.load(); + } catch (IOException e) { + System.err.println("Error Loading DDMS Preferences"); + } + } + } + + assert sPrefStore != null; + return sPrefStore; + } + } + + /** + * Save the prefs to the config file. + */ + public void save() { + PreferenceStore prefs = getPreferenceStore(); + synchronized(sLock) { + try { + prefs.save(); + } + catch (IOException ioe) { + // FIXME com.android.dmmlib.Log.w("ddms", "Failed saving prefs file: " + ioe.getMessage()); + } + } + } + + // ---- Utility methods to access some specifis prefs ---- + + /** + * Indicates whether the ping ID is set. + * This should be true when {@link #isPingOptIn()} is true. + * + * @return true if a ping ID is set, which means the user gave permission + * to use the ping service. + */ + public boolean hasPingId() { + PreferenceStore prefs = getPreferenceStore(); + synchronized(sLock) { + return prefs != null && prefs.contains(PING_ID); + } + } + + /** + * Retrieves the current ping ID, if set. + * To know if the ping ID is set, use {@link #hasPingId()}. + * The value returned is 0L if there's no store or no ping ID set in the store. + */ + public long getPingId() { + PreferenceStore prefs = getPreferenceStore(); + synchronized(sLock) { + return prefs == null ? 0L : prefs.getLong(PING_ID); + } + } + + /** + * Generates a new random ping ID and saves it in the preference store. + * + * @return The new ping ID. + */ + public long generateNewPingId() { + PreferenceStore prefs = getPreferenceStore(); + + Random rnd = new Random(); + long id = rnd.nextLong(); + + // Let's try to be conservative and avoid 0L as an ID. + // (ideally it would be nice to keep 0L as a special reserved value, + // to both have a default value and detect when variables aren't set + // properly. It's too late to enforce this, but we can still avoid + // generating new ones like this.) + for (int i = 0; id == 0L && i < 10; i++) { + id = rnd.nextLong(); + } + + synchronized(sLock) { + prefs.setValue(PING_ID, id); + try { + prefs.save(); + } catch (IOException e) { + /* ignore exceptions while saving preferences */ + } + } + + return id; + } + + /** + * Returns the "ping opt in" value from the preference store. + * This would be true if there's a valid preference store and + * the user opted for sending ping statistics. + */ + public boolean isPingOptIn() { + PreferenceStore prefs = getPreferenceStore(); + synchronized(sLock) { + return prefs != null && prefs.contains(PING_OPT_IN); + } + } + + /** + * Saves the "ping opt in" value in the preference store. + * + * @param optIn The new user opt-in value. + */ + public void setPingOptIn(boolean optIn) { + PreferenceStore prefs = getPreferenceStore(); + + synchronized(sLock) { + prefs.setValue(PING_OPT_IN, optIn); + try { + prefs.save(); + } catch (IOException e) { + /* ignore exceptions while saving preferences */ + } + } + } + + /** + * Retrieves the ping time for the given app from the preference store. + * Callers should use {@link System#currentTimeMillis()} for time stamps. + * + * @param app The app name identifier. + * @return 0L if we don't have a preference store or there was no time + * recorded in the store for the requested app. Otherwise the time stamp + * from the store. + */ + public long getPingTime(String app) { + PreferenceStore prefs = getPreferenceStore(); + String timePref = PING_TIME + "." + app; //$NON-NLS-1$ + synchronized(sLock) { + return prefs == null ? 0 : prefs.getLong(timePref); + } + } + + /** + * Sets the ping time for the given app from the preference store. + * Callers should use {@link System#currentTimeMillis()} for time stamps. + * + * @param app The app name identifier. + * @param timeStamp The time stamp from the store. + * 0L is a sepcial value that should not be used. + */ + public void setPingTime(String app, long timeStamp) { + PreferenceStore prefs = getPreferenceStore(); + String timePref = PING_TIME + "." + app; //$NON-NLS-1$ + synchronized(sLock) { + prefs.setValue(timePref, timeStamp); + try { + prefs.save(); + } catch (IOException ioe) { + /* ignore exceptions while saving preferences */ + } + } + } + + /** + * True if there's a valid preference store and the ADT startup + * wizard has been shown once. + */ + public boolean wasAdtStartupWizardShown() { + PreferenceStore prefs = getPreferenceStore(); + synchronized(sLock) { + return prefs == null ? false : prefs.getBoolean(ADT_STARTUP_WIZARD); + } + } + + /** + * Sets whether the ADT startup wizard has been shown. + */ + public void setAdtStartupWizardShown(boolean shown) { + PreferenceStore prefs = getPreferenceStore(); + synchronized(sLock) { + prefs.setValue(ADT_STARTUP_WIZARD, shown); + try { + prefs.save(); + } catch (IOException ioe) { + /* ignore exceptions while saving preferences */ + } + } + } + + /** + * Retrieves the last SDK OS path. + *

+ * This is just an information value, the path may not exist, may not + * even be on an existing file system and/or may not point to an SDK + * anymore. + * + * @return The last SDK OS path from the preference store, or null if + * there is no store or an empty string if it is not defined. + */ + public String getLastSdkPath() { + PreferenceStore prefs = getPreferenceStore(); + synchronized(sLock) { + return prefs == null ? null : prefs.getString(LAST_SDK_PATH); + } + } + + /** + * Sets the last SDK OS path. + * + * @param osSdkPath The SDK OS Path. Can be null or empty. + */ + public void setLastSdkPath(String osSdkPath) { + PreferenceStore prefs = getPreferenceStore(); + synchronized(sLock) { + prefs.setValue(LAST_SDK_PATH, osSdkPath); + try { + prefs.save(); + } catch (IOException ioe) { + /* ignore exceptions while saving preferences */ + } + } + } +} diff --git a/sdkstats/src/com/android/sdkstats/SdkStatsService.java b/sdkstats/src/com/android/sdkstats/SdkStatsService.java index 25a7d9777..b1b8c4776 100644 --- a/sdkstats/src/com/android/sdkstats/SdkStatsService.java +++ b/sdkstats/src/com/android/sdkstats/SdkStatsService.java @@ -16,20 +16,13 @@ package com.android.sdkstats; -import com.android.prefs.AndroidLocation; -import com.android.prefs.AndroidLocation.AndroidLocationException; - -import org.eclipse.jface.preference.PreferenceStore; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; -import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -39,11 +32,10 @@ public class SdkStatsService { /** Minimum interval between ping, in milliseconds. */ private static final long PING_INTERVAL_MSEC = 86400 * 1000; // 1 day - public final static String PING_OPT_IN = "pingOptIn"; //$NON-NLS-1$ - public final static String PING_TIME = "pingTime"; //$NON-NLS-1$ - public final static String PING_ID = "pingId"; //$NON-NLS-1$ + private DdmsPreferenceStore mStore = new DdmsPreferenceStore(); - private static PreferenceStore sPrefStore; + public SdkStatsService() { + } /** * Send a "ping" to the Google toolbar server, if enough time has @@ -58,19 +50,8 @@ public class SdkStatsService { * @param app name to report in the ping * @param version to report in the ping */ - public static void ping(final String app, final String version) { - doPing(app, version, getPreferenceStore()); - } - - /** - * Find out if user has already set permissions for the ping service. - * @return true if user has already set the permissions for the ping service. This could've - * happened only if the user has already viewed the dialog displayed by - * {@link #getUserPermissionForPing(Shell)}. - */ - public static boolean pingPermissionsSet() { - PreferenceStore prefs = getPreferenceStore(); - return prefs != null && prefs.contains(PING_ID); + public void ping(String app, String version) { + doPing(app, version); } /** @@ -80,108 +61,60 @@ public class SdkStatsService { * Once the dialog has been shown, it sets a preference internally indicating that the user has * viewed this dialog. This setting can be queried using {@link #pingPermissionsSet()}. */ - public static void getUserPermissionForPing(Shell parent) { - PreferenceStore prefs = getPreferenceStore(); - getUserPermissionForPing(prefs, parent); - - // First time: make up a new ID. TODO: Use something more random? - prefs.setValue(PING_ID, new Random().nextLong()); - try { - prefs.save(); - } catch (IOException e) { - /* ignore exceptions while saving preferences */ + public void checkUserPermissionForPing(Shell parent) { + if (!mStore.hasPingId()) { + askUserPermissionForPing(parent); + mStore.generateNewPingId(); } } /** - * Returns the DDMS {@link PreferenceStore}. + * Prompt the user for whether they want to opt out of reporting, and save the user + * input in preferences. */ - public static synchronized PreferenceStore getPreferenceStore() { - if (sPrefStore == null) { - // get the location of the preferences - String homeDir = null; - try { - homeDir = AndroidLocation.getFolder(); - } catch (AndroidLocationException e1) { - // pass, we'll do a dummy store since homeDir is null - } - - if (homeDir != null) { - String rcFileName = homeDir + "ddms.cfg"; //$NON-NLS-1$ - - // also look for an old pref file in the previous location - String oldPrefPath = System.getProperty("user.home") //$NON-NLS-1$ - + File.separator + ".ddmsrc"; //$NON-NLS-1$ - File oldPrefFile = new File(oldPrefPath); - if (oldPrefFile.isFile()) { - try { - PreferenceStore oldStore = new PreferenceStore(oldPrefPath); - oldStore.load(); - - oldStore.save(new FileOutputStream(rcFileName), ""); - oldPrefFile.delete(); - - PreferenceStore newStore = new PreferenceStore(rcFileName); - newStore.load(); - sPrefStore = newStore; - } catch (IOException e) { - // create a new empty store. - sPrefStore = new PreferenceStore(rcFileName); - } - } else { - sPrefStore = new PreferenceStore(rcFileName); - - try { - sPrefStore.load(); - } catch (IOException e) { - System.err.println("Error Loading Preferences"); - } - } - } else { - sPrefStore = new PreferenceStore(); + private void askUserPermissionForPing(final Shell parent) { + final Display display = parent.getDisplay(); + display.syncExec(new Runnable() { + public void run() { + SdkStatsPermissionDialog dialog = new SdkStatsPermissionDialog(parent); + dialog.open(); + mStore.setPingOptIn(dialog.getPingUserPreference()); } - } - - return sPrefStore; + }); } + // ------- + /** * Pings the usage stats server, as long as the prefs contain the opt-in boolean * * @param app name to report in the ping * @param version to report in the ping - * @param prefs the preference store where the opt-in value and ping times are store */ - private static void doPing(final String app, String version, PreferenceStore prefs) { + private void doPing(final String app, String version) { // Validate the application and version input. final String normalVersion = normalizeVersion(app, version); // If the user has not opted in, do nothing and quietly return. - if (!prefs.getBoolean(PING_OPT_IN)) { + if (!mStore.isPingOptIn()) { // user opted out. return; } // If the last ping *for this app* was too recent, do nothing. - String timePref = PING_TIME + "." + app; //$NON-NLS-1$ long now = System.currentTimeMillis(); - long then = prefs.getLong(timePref); + long then = mStore.getPingTime(app); if (now - then < PING_INTERVAL_MSEC) { // too soon after a ping. return; } // Record the time of the attempt, whether or not it succeeds. - prefs.setValue(timePref, now); - try { - prefs.save(); - } - catch (IOException ioe) { - } + mStore.setPingTime(app, now); // Send the ping itself in the background (don't block if the // network is down or slow or confused). - final long id = prefs.getLong(PING_ID); + final long id = mStore.getPingId(); new Thread() { @Override public void run() { @@ -269,21 +202,6 @@ public class SdkStatsService { } /** - * Prompt the user for whether they want to opt out of reporting, and save the user - * input in preferences. - */ - private static void getUserPermissionForPing(final PreferenceStore prefs, final Shell parent) { - final Display display = parent.getDisplay(); - display.syncExec(new Runnable() { - public void run() { - SdkStatsPermissionDialog dialog = new SdkStatsPermissionDialog(parent); - dialog.open(); - prefs.setValue(PING_OPT_IN, dialog.getPingUserPreference()); - } - }); - } - - /** * Validate the supplied application version, and normalize the version. * @param app to report * @param version supplied by caller