diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:16 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:16 -0800 |
commit | 82ea7a177797b844b252effea5c7c7c5d63ea4ac (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /sdkstats/src | |
parent | c9432be76d50a527da232d518f633add2f76242b (diff) | |
download | sdk-82ea7a177797b844b252effea5c7c7c5d63ea4ac.zip sdk-82ea7a177797b844b252effea5c7c7c5d63ea4ac.tar.gz sdk-82ea7a177797b844b252effea5c7c7c5d63ea4ac.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'sdkstats/src')
-rw-r--r-- | sdkstats/src/Android.mk | 15 | ||||
-rw-r--r-- | sdkstats/src/com/android/sdkstats/SdkStatsService.java | 416 |
2 files changed, 0 insertions, 431 deletions
diff --git a/sdkstats/src/Android.mk b/sdkstats/src/Android.mk deleted file mode 100644 index bff43f3..0000000 --- a/sdkstats/src/Android.mk +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2007 The Android Open Source Project -# -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_JAVA_LIBRARIES := \ - androidprefs \ - swt \ - org.eclipse.jface_3.2.0.I20060605-1400 \ - org.eclipse.equinox.common_3.2.0.v20060603 \ - org.eclipse.core.commands_3.2.0.I20060605-1400 -LOCAL_MODULE := sdkstats - -include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/sdkstats/src/com/android/sdkstats/SdkStatsService.java b/sdkstats/src/com/android/sdkstats/SdkStatsService.java deleted file mode 100644 index 0b3d41b..0000000 --- a/sdkstats/src/com/android/sdkstats/SdkStatsService.java +++ /dev/null @@ -1,416 +0,0 @@ -/* - * 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 org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.Font; -import org.eclipse.swt.graphics.FontData; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.program.Program; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Link; -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; - -/** Utility class to send "ping" usage reports to the server. */ -public class SdkStatsService { - - /** Minimum interval between ping, in milliseconds. */ - private static final long PING_INTERVAL_MSEC = 86400 * 1000; // 1 day - - /* Text strings displayed in the opt-out dialog. */ - private static final String WINDOW_TITLE_TEXT = - "Android SDK"; - - private static final String HEADER_TEXT = - "Thanks for using the Android SDK!"; - - private static final String NOTICE_TEXT = - "We know you just want to get started but please read this first."; - - /** Used in the preference pane (PrefsDialog) as well. */ - public static final String BODY_TEXT = - "By choosing to send certain usage statistics to Google, you can " + - "help us improve the Android SDK. These usage statistics let us " + - "measure things like active usage of the SDK and let us know things " + - "like which versions of the SDK are in use and which tools are the " + - "most popular with developers. This limited data is not associated " + - "with personal information about you, is examined on an aggregate " + - "basis, and is maintained in accordance with the " + - "<a href=\"http://www.google.com/intl/en/privacy.html\">Google " + - "Privacy Policy</a>."; - - /** Used in the preference pane (PrefsDialog) as well. */ - public static final String CHECKBOX_TEXT = - "Send usage statistics to Google."; - - private static final String FOOTER_TEXT = - "If you later decide to change this setting, you can do so in the " + - "\"ddms\" tool under \"File\" > \"Preferences\" > \"Usage Stats\"."; - - private static final String BUTTON_TEXT = - " Proceed "; - - /** List of Linux browser commands to try, in order (see openUrl). */ - private static final String[] LINUX_BROWSERS = new String[] { - "firefox -remote openurl(%URL%,new-window)", // $NON-NLS-1$ running FF - "mozilla -remote openurl(%URL%,new-window)", // $NON-NLS-1$ running Moz - "firefox %URL%", // $NON-NLS-1$ new FF - "mozilla %URL%", // $NON-NLS-1$ new Moz - "kfmclient openURL %URL%", // $NON-NLS-1$ Konqueror - "opera -newwindow %URL%", // $NON-NLS-1$ Opera - }; - - 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 static PreferenceStore sPrefStore; - - /** - * Send a "ping" to the Google toolbar server, if enough time has - * elapsed since the last ping, and if the user has not opted out. - * If this is the first time, notify the user and offer an opt-out. - * Note: UI operations (if any) are synchronous, but the actual ping - * (if any) is sent in a <i>non-daemon</i> background thread. - * - * @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) { - // Validate the application and version input. - final String normalVersion = normalizeVersion(app, version); - - // Unique, randomly assigned ID for this installation. - PreferenceStore prefs = getPreferenceStore(); - if (prefs != null) { - if (!prefs.contains(PING_ID)) { - // First time: make up a new ID. TODO: Use something more random? - prefs.setValue(PING_ID, new Random().nextLong()); - - // Also give them a chance to opt out. - prefs.setValue(PING_OPT_IN, getUserPermission()); - try { - prefs.save(); - } - catch (IOException ioe) { - } - } - - // If the user has not opted in, do nothing and quietly return. - if (!prefs.getBoolean(PING_OPT_IN)) { - // 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); - 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) { - } - - // 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); - new Thread() { - @Override - public void run() { - try { - actuallySendPing(app, normalVersion, id); - } catch (IOException e) { - e.printStackTrace(); - } - } - }.start(); - } - } - - /** - * Returns the DDMS {@link PreferenceStore}. - */ - 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(); - } - } - - return sPrefStore; - } - - /** - * Unconditionally send a "ping" request to the Google toolbar server. - * - * @param app name to report in the ping - * @param version to report in the ping (dotted numbers, no more than four) - * @param id of the local installation - * @throws IOException if the ping failed - */ - @SuppressWarnings("deprecation") - private static void actuallySendPing(String app, String version, long id) - throws IOException { - // Detect and report the host OS. - String os = System.getProperty("os.name"); // $NON-NLS-1$ - if (os.startsWith("Mac OS")) { // $NON-NLS-1$ - os = "mac"; // $NON-NLS-1$ - } else if (os.startsWith("Windows")) { // $NON-NLS-1$ - os = "win"; // $NON-NLS-1$ - } else if (os.startsWith("Linux")) { // $NON-NLS-1$ - os = "linux"; // $NON-NLS-1$ - } else { - // Unknown -- surprising -- send it verbatim so we can see it. - os = URLEncoder.encode(os); - } - - // Include the application's name as part of the as= value. - // Share the user ID for all apps, to allow unified activity reports. - - URL url = new URL( - "http", // $NON-NLS-1$ - "tools.google.com", // $NON-NLS-1$ - "/service/update?as=androidsdk_" + app + // $NON-NLS-1$ - "&id=" + Long.toHexString(id) + // $NON-NLS-1$ - "&version=" + version + // $NON-NLS-1$ - "&os=" + os); // $NON-NLS-1$ - - // Discard the actual response, but make sure it reads OK - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - - // Believe it or not, a 404 response indicates success: - // the ping was logged, but no update is configured. - if (conn.getResponseCode() != HttpURLConnection.HTTP_OK && - conn.getResponseCode() != HttpURLConnection.HTTP_NOT_FOUND) { - throw new IOException( - conn.getResponseMessage() + ": " + url); // $NON-NLS-1$ - } - } - - /** - * Prompt the user for whether they want to opt out of reporting. - * @return whether the user allows reporting (they do not opt out). - */ - private static boolean getUserPermission() { - // Use dialog trim for the shell, but without a close button. - final Display display = new Display(); - final Shell shell = new Shell(display, SWT.TITLE | SWT.BORDER); - shell.setText(WINDOW_TITLE_TEXT); - shell.setLayout(new GridLayout(1, false)); // 1 column - - // Take the default font and scale it up for the title. - final Label title = new Label(shell, SWT.CENTER | SWT.WRAP); - final FontData[] fontdata = title.getFont().getFontData(); - for (int i = 0; i < fontdata.length; i++) { - fontdata[i].setHeight(fontdata[i].getHeight() * 4 / 3); - } - title.setFont(new Font(display, fontdata)); - title.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - title.setText(HEADER_TEXT); - - final Label notice = new Label(shell, SWT.WRAP); - notice.setFont(title.getFont()); - notice.setForeground(new Color(display, 255, 0, 0)); - notice.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - notice.setText(NOTICE_TEXT); - - final Link text = new Link(shell, SWT.WRAP); - text.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - text.setText(BODY_TEXT); - text.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent event) { - openUrl(event.text); - } - }); - - final Button checkbox = new Button(shell, SWT.CHECK); - checkbox.setSelection(true); // Opt-in by default. - checkbox.setText(CHECKBOX_TEXT); - - final Link footer = new Link(shell, SWT.WRAP); - footer.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - footer.setText(FOOTER_TEXT); - - // Whether the user gave permission (size-1 array for writing to). - // Initialize to false, set when the user clicks the button. - final boolean[] permission = new boolean[] { false }; - - final Button button = new Button(shell, SWT.PUSH); - button.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER)); - button.setText(BUTTON_TEXT); - button.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent event) { - permission[0] = checkbox.getSelection(); - shell.close(); - } - }); - - // Size the window to a fixed width, as high as necessary, centered. - final Point size = shell.computeSize(450, SWT.DEFAULT, true); - final Rectangle screen = display.getClientArea(); - shell.setBounds( - screen.x + screen.width / 2 - size.x / 2, - screen.y + screen.height / 2 - size.y / 2, - size.x, size.y); - - shell.open(); - while (!shell.isDisposed()) { - if (!display.readAndDispatch()) - display.sleep(); - } - - display.dispose(); // Otherwise ddms' own Display can't be created - return permission[0]; - } - - /** - * Open a URL in an external browser. - * @param url to open - MUST be sanitized and properly formed! - */ - public static void openUrl(final String url) { - // TODO: consider using something like BrowserLauncher2 - // (http://browserlaunch2.sourceforge.net/) instead of these hacks. - - // SWT's Program.launch() should work on Mac, Windows, and GNOME - // (because the OS shell knows how to launch a default browser). - if (!Program.launch(url)) { - // Must be Linux non-GNOME (or something else broke). - // Try a few Linux browser commands in the background. - new Thread() { - @Override - public void run() { - for (String cmd : LINUX_BROWSERS) { - cmd = cmd.replaceAll("%URL%", url); // $NON-NLS-1$ - try { - Process proc = Runtime.getRuntime().exec(cmd); - if (proc.waitFor() == 0) break; // Success! - } catch (InterruptedException e) { - // Should never happen! - throw new RuntimeException(e); - } catch (IOException e) { - // Swallow the exception and try the next browser. - } - } - - // TODO: Pop up some sort of error here? - // (We're in a new thread; can't use the existing Display.) - } - }.start(); - } - } - - /** - * Validate the supplied application version, and normalize the version. - * @param app to report - * @param version supplied by caller - * @return normalized dotted quad version - */ - private static String normalizeVersion(String app, String version) { - // Application name must contain only word characters (no punctuaation) - if (!app.matches("\\w+")) { - throw new IllegalArgumentException("Bad app name: " + app); - } - - // Version must be between 1 and 4 dotted numbers - String[] numbers = version.split("\\."); - if (numbers.length > 4) { - throw new IllegalArgumentException("Bad version: " + version); - } - for (String part: numbers) { - if (!part.matches("\\d+")) { - throw new IllegalArgumentException("Bad version: " + version); - } - } - - // Always output 4 numbers, even if fewer were supplied (pad with .0) - StringBuffer normal = new StringBuffer(numbers[0]); - for (int i = 1; i < 4; i++) { - normal.append(".").append(i < numbers.length ? numbers[i] : "0"); - } - return normal.toString(); - } -} |