auto import from //depot/cupcake/@135843
This commit is contained in:
@@ -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)
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user