auto import from //branches/cupcake/...@130745

This commit is contained in:
The Android Open Source Project
2009-02-10 15:43:58 -08:00
parent 5a4d0fa291
commit e3c5766074
95 changed files with 6116 additions and 2460 deletions

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2009 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="appTitle">"Fallback"</string>
<string name="title">"지원되지 않는 작업"</string>
<string name="error">"이 작업은 현재 지원되지 않습니다."</string>
</resources>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright (C) 2009 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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="appTitle">"Fallback"</string>
<string name="title">"Ustøttet handling"</string>
<string name="error">"Denne handlingen er ikke støttet nå."</string>
</resources>

View File

@@ -30,6 +30,10 @@
<string name="summary_transition_animations">Speed of animations moving between screens</string>
<string name="dialog_title_transition_animations">Select transition speed</string>
<string name="title_fancy_ime_animations">Fancy input animations</string>
<string name="summary_on_fancy_ime_animations">Use fancier animations for input method windows</string>
<string name="summary_off_fancy_ime_animations">Use normal animations for input method windows</string>
<string name="title_font_size">Font size</string>
<string name="summary_font_size">Overall size of fonts</string>
<string name="dialog_title_font_size">Select font size</string>

View File

@@ -37,6 +37,12 @@
android:entryValues="@array/entryvalues_animations"
android:dialogTitle="@string/dialog_title_transition_animations" />
<CheckBoxPreference
android:key="fancy_ime_animations"
android:title="@string/title_fancy_ime_animations"
android:summaryOn="@string/summary_on_fancy_ime_animations"
android:summaryOff="@string/summary_off_fancy_ime_animations"/>
<ListPreference
android:key="font_size"
android:title="@string/title_font_size"

View File

@@ -42,6 +42,7 @@ public class SpareParts extends PreferenceActivity
private static final String WINDOW_ANIMATIONS_PREF = "window_animations";
private static final String TRANSITION_ANIMATIONS_PREF = "transition_animations";
private static final String FANCY_IME_ANIMATIONS_PREF = "fancy_ime_animations";
private static final String FONT_SIZE_PREF = "font_size";
private static final String END_BUTTON_PREF = "end_button";
private static final String ACCELEROMETER_PREF = "accelerometer";
@@ -51,6 +52,7 @@ public class SpareParts extends PreferenceActivity
private ListPreference mWindowAnimationsPref;
private ListPreference mTransitionAnimationsPref;
private CheckBoxPreference mFancyImeAnimationsPref;
private ListPreference mFontSizePref;
private ListPreference mEndButtonPref;
private CheckBoxPreference mAccelerometerPref;
@@ -69,6 +71,7 @@ public class SpareParts extends PreferenceActivity
mWindowAnimationsPref.setOnPreferenceChangeListener(this);
mTransitionAnimationsPref = (ListPreference) prefSet.findPreference(TRANSITION_ANIMATIONS_PREF);
mTransitionAnimationsPref.setOnPreferenceChangeListener(this);
mFancyImeAnimationsPref = (CheckBoxPreference) prefSet.findPreference(FANCY_IME_ANIMATIONS_PREF);
mFontSizePref = (ListPreference) prefSet.findPreference(FONT_SIZE_PREF);
mFontSizePref.setOnPreferenceChangeListener(this);
mEndButtonPref = (ListPreference) prefSet.findPreference(END_BUTTON_PREF);
@@ -83,6 +86,9 @@ public class SpareParts extends PreferenceActivity
private void updateToggles() {
try {
mFancyImeAnimationsPref.setChecked(Settings.System.getInt(
getContentResolver(),
Settings.System.FANCY_IME_ANIMATIONS, 0) != 0);
mAccelerometerPref.setChecked(Settings.System.getInt(
getContentResolver(),
Settings.System.ACCELEROMETER_ROTATION, 0) != 0);
@@ -181,6 +187,10 @@ public class SpareParts extends PreferenceActivity
Settings.System.putInt(getContentResolver(),
Settings.System.ACCELEROMETER_ROTATION,
mAccelerometerPref.isChecked() ? 1 : 0);
} else if (FANCY_IME_ANIMATIONS_PREF.equals(key)) {
Settings.System.putInt(getContentResolver(),
Settings.System.FANCY_IME_ANIMATIONS,
mFancyImeAnimationsPref.isChecked() ? 1 : 0);
} else if (MAPS_COMPASS_PREF.equals(key)) {
try {
Context c = createPackageContext("com.google.android.apps.maps", 0);

View File

@@ -4,7 +4,8 @@
<activity android:name="Term"
android:theme="@style/Theme"
android:launchMode="singleInstance"
android:configChanges="keyboard|keyboardHidden|orientation">
android:configChanges="keyboard|keyboardHidden|orientation"
android:windowSoftInputMode="adjustResize|stateVisible">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.TEST" />

View File

@@ -48,6 +48,12 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -177,6 +183,9 @@ public class Term extends Activity {
mKeyListener = new TermKeyListener();
mEmulatorView.setFocusable(true);
mEmulatorView.requestFocus();
updatePrefs();
}
@@ -2583,7 +2592,6 @@ class EmulatorView extends View implements GestureDetector.OnGestureListener {
private FileOutputStream mTermOut;
private ByteQueue mByteQueue;
private final static int MAX_BYTES_PER_UPDATE = 4 * 1024;
/**
* Used to temporarily hold data received from the remote process. Allocated
@@ -2643,6 +2651,117 @@ class EmulatorView extends View implements GestureDetector.OnGestureListener {
invalidate();
}
@Override
public boolean onCheckIsTextEditor() {
return true;
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return new InputConnection(){
public boolean beginBatchEdit() {
return true;
}
public boolean clearMetaKeyStates(int states) {
return true;
}
public boolean commitCompletion(CompletionInfo text) {
return true;
}
public boolean commitText(CharSequence text, int newCursorPosition) {
sendText(text);
return true;
}
public boolean deleteSurroundingText(int leftLength, int rightLength) {
return true;
}
public boolean endBatchEdit() {
return true;
}
public boolean finishComposingText() {
return true;
}
public int getCursorCapsMode(int reqModes) {
return 0;
}
public ExtractedText getExtractedText(ExtractedTextRequest request,
int flags) {
return null;
}
public CharSequence getTextAfterCursor(int n, int flags) {
return null;
}
public CharSequence getTextBeforeCursor(int n, int flags) {
return null;
}
public boolean hideStatusIcon() {
return true;
}
public boolean performContextMenuAction(int id) {
return true;
}
public boolean performPrivateCommand(String action, Bundle data) {
return true;
}
public boolean sendKeyEvent(KeyEvent event) {
switch(event.getKeyCode()) {
case KeyEvent.KEYCODE_ENTER:
sendChar('\r');
break;
case KeyEvent.KEYCODE_DEL:
sendChar(127);
break;
}
return true;
}
public boolean setComposingText(CharSequence text, int newCursorPosition) {
return true;
}
public boolean setSelection(int start, int end) {
return true;
}
public boolean showStatusIcon(String packageName, int resId) {
return true;
}
private void sendChar(int c) {
try {
mTermOut.write(c);
} catch (IOException ex) {
}
}
private void sendText(CharSequence text) {
int n = text.length();
try {
for(int i = 0; i < n; i++) {
char c = text.charAt(i);
mTermOut.write(c);
}
} catch (IOException e) {
}
}
};
}
public boolean getKeypadApplicationMode() {
return mEmulator.getKeypadApplicationMode();
}

View File

@@ -126,7 +126,7 @@ system.img platforms/${PLATFORM_NAME}/images/system.img
ramdisk.img platforms/${PLATFORM_NAME}/images/ramdisk.img
userdata.img platforms/${PLATFORM_NAME}/images/userdata.img
prebuilt/android-arm/kernel/kernel-qemu platforms/${PLATFORM_NAME}/images/kernel-qemu
external/qemu/android/vm/hardware-properties.ini tools/lib/hardware-properties.ini
external/qemu/android/avd/hardware-properties.ini tools/lib/hardware-properties.ini
# emulator skins
development/emulator/skins/HVGA platforms/${PLATFORM_NAME}/skins/HVGA

Binary file not shown.

View File

@@ -10,7 +10,7 @@ Signature="$WINDOWS NT$"
Class=USB
ClassGuid={F72FE0D4-CBCB-407d-8814-9ED673D0DD6B}
Provider=%GOOG%
DriverVer=12/11/2008,1.0.0009.00000
DriverVer=1/29/2009,1.0.0010.00000
CatalogFile.NTx86=androidusb86.cat
CatalogFile.NTamd64=androidusba64.cat
@@ -38,6 +38,7 @@ DefaultDestDir = 12
; HTC Dream
%USB\VID_0BB4&PID_0C01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C01
%USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C02&MI_01
%USB\VID_0BB4&PID_0FFF.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0FFF
; For XP and later
[Google.NTx86]
@@ -46,6 +47,7 @@ DefaultDestDir = 12
; HTC Dream
%USB\VID_0BB4&PID_0C01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C01
%USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C02&MI_01
%USB\VID_0BB4&PID_0FFF.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0FFF
; For AMD64 and later
[Google.NTamd64]
@@ -54,6 +56,7 @@ DefaultDestDir = 12
; HTC Dream
%USB\VID_0BB4&PID_0C01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C01
%USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C02&MI_01
%USB\VID_0BB4&PID_0FFF.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0FFF
[androidusb.Dev.NT]
CopyFiles=androidusb.Files.Ext
@@ -120,3 +123,4 @@ ClassName = "ADB Interface"
USB\VID_18D1&PID_DDDD.DeviceDescTest="ADB Testing Interface"
USB\VID_0BB4&PID_0C01.DeviceDescRelease="HTC Dream"
USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease="HTC Dream Composite ADB Interface"
USB\VID_0BB4&PID_0FFF.DeviceDescRelease="HTC Bootloader"

View File

@@ -10,7 +10,7 @@ Signature="$WINDOWS NT$"
Class=USB
ClassGuid={F72FE0D4-CBCB-407d-8814-9ED673D0DD6B}
Provider=%GOOG%
DriverVer=12/11/2008,1.0.0009.00000
DriverVer=1/29/2009,1.0.0010.00000
CatalogFile.NTx86=androidusb86.cat
CatalogFile.NTamd64=androidusba64.cat
@@ -38,6 +38,7 @@ DefaultDestDir = 12
; HTC Dream
%USB\VID_0BB4&PID_0C01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C01
%USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C02&MI_01
%USB\VID_0BB4&PID_0FFF.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0FFF
; For XP and later
[Google.NTx86]
@@ -46,6 +47,7 @@ DefaultDestDir = 12
; HTC Dream
%USB\VID_0BB4&PID_0C01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C01
%USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C02&MI_01
%USB\VID_0BB4&PID_0FFF.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0FFF
; For AMD64 and later
[Google.NTamd64]
@@ -54,6 +56,7 @@ DefaultDestDir = 12
; HTC Dream
%USB\VID_0BB4&PID_0C01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C01
%USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C02&MI_01
%USB\VID_0BB4&PID_0FFF.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0FFF
[androidusb.Dev.NT]
CopyFiles=androidusb.Files.Ext
@@ -120,3 +123,4 @@ ClassName = "ADB Interface"
USB\VID_18D1&PID_DDDD.DeviceDescTest="ADB Testing Interface"
USB\VID_0BB4&PID_0C01.DeviceDescRelease="HTC Dream"
USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease="HTC Dream Composite ADB Interface"
USB\VID_0BB4&PID_0FFF.DeviceDescRelease="HTC Bootloader"

View File

@@ -106,7 +106,7 @@ bool AdbInterfaceObject::GetSerialNumber(void* buffer,
// Open USB device for this intefface
HANDLE usb_device_handle = CreateFile(interface_name().c_str(),
FILE_READ_ATTRIBUTES | FILE_READ_EA,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
@@ -208,7 +208,7 @@ bool AdbInterfaceObject::GetEndpointInformation(UCHAR endpoint_index,
// Open USB device for this intefface
HANDLE usb_device_handle = CreateFile(interface_name().c_str(),
FILE_READ_ATTRIBUTES | FILE_READ_EA,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,

View File

@@ -10,7 +10,7 @@ Signature="$WINDOWS NT$"
Class=USB
ClassGuid={F72FE0D4-CBCB-407d-8814-9ED673D0DD6B}
Provider=%GOOG%
DriverVer=12/11/2008,1.0.0009.00000
DriverVer=1/29/2009,1.0.0010.00000
CatalogFile.NTx86=androidusb86.cat
CatalogFile.NTamd64=androidusba64.cat
@@ -38,6 +38,7 @@ DefaultDestDir = 12
; HTC Dream
%USB\VID_0BB4&PID_0C01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C01
%USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C02&MI_01
%USB\VID_0BB4&PID_0FFF.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0FFF
; For XP and later
[Google.NTx86]
@@ -46,6 +47,7 @@ DefaultDestDir = 12
; HTC Dream
%USB\VID_0BB4&PID_0C01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C01
%USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C02&MI_01
%USB\VID_0BB4&PID_0FFF.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0FFF
; For AMD64 and later
[Google.NTamd64]
@@ -54,6 +56,7 @@ DefaultDestDir = 12
; HTC Dream
%USB\VID_0BB4&PID_0C01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C01
%USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0C02&MI_01
%USB\VID_0BB4&PID_0FFF.DeviceDescRelease%=androidusb.Dev, USB\VID_0BB4&PID_0FFF
[androidusb.Dev.NT]
CopyFiles=androidusb.Files.Ext
@@ -120,3 +123,4 @@ ClassName = "ADB Interface"
USB\VID_18D1&PID_DDDD.DeviceDescTest="ADB Testing Interface"
USB\VID_0BB4&PID_0C01.DeviceDescRelease="HTC Dream"
USB\VID_0BB4&PID_0C02&MI_01.DeviceDescRelease="HTC Dream Composite ADB Interface"
USB\VID_0BB4&PID_0FFF.DeviceDescRelease="HTC Bootloader"

View File

@@ -19,7 +19,6 @@
<classpathentry kind="src" path="packages/apps/Settings/src"/>
<classpathentry kind="src" path="packages/apps/SoundRecorder/src"/>
<classpathentry kind="src" path="packages/apps/Stk/src"/>
<classpathentry kind="src" path="packages/apps/Sync/src"/>
<classpathentry kind="src" path="packages/apps/Updater/src"/>
<classpathentry kind="src" path="packages/apps/VoiceDialer/src"/>
<classpathentry kind="src" path="packages/providers/CalendarProvider/src"/>

View File

@@ -493,22 +493,27 @@ include $(BUILD_PACKAGE)
<a name="androidTestingContentManifest"></a><h3>Content of Manifest</h3>
<p>Use the following example to create an <code>AndroidManifest.xml</code> file that declares the instrumentation. Specify that the framework supplied Instrumentation TestRunner targest the package of your application, allowing the tests that are run with the instrumentation to get access to all of the classes of your application without having to build the source into the test app. The name of the test application is typically the same as your target application with <code>.tests</code> appended. </p>
<p>Use the following example to create an <code>AndroidManifest.xml</code> file that declares the instrumentation. Specify that the framework supplied InstrumentationTestRunner targets the package of your application, allowing the tests that are run with the instrumentation to get access to all of the classes of your application without having to build the source into the test app. The name of the test application is typically the same as your target application with <code>.tests</code> appended. </p>
<pre>
# Add appropriate copyright banner here
&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.samples.tests"&gt;
package="com.example.android.apis.tests"&gt;
&lt;uses-permission android:name="android.permission.RUN_INSTRUMENTATION" /&gt;
&lt;!-- We add an application tag here just so that we can indicate that
this package needs to link against the android.test library,
which is needed when building test cases. -->
&lt;application>
&lt;uses-library android:name="android.test.runner" />
&lt;/application>
&lt;!--
This declares that this app uses the instrumentation test runner targeting
the package of com.android.samples. To run the tests use the command:
"adb shell am instrument -w com.android.samples.tests/android.test.InstrumentationTestRunner"
--&gt;
the package of com.example.android.apis. To run the tests use the command:
"adb shell am instrument -w com.example.android.apis.tests/android.test.InstrumentationTestRunner"
-->
&lt;instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.android.samples"
android:label="Tests for Api Demos."/&gt;
android:targetPackage="com.example.android.apis"
android:label="Tests for Api Demos."/>
&lt;/manifest&gt;
</pre>
@@ -544,8 +549,6 @@ public class FrameworkInstrumentationTestRunner extends InstrumentationTestRunne
</pre>
<p> Next, in an appropriate <code>AndroidManifest.xml</code>, define the instrumentation for the derived class with the appropriate <code>android:targetPackage</code> set. For example, the snippet below defines the instrumentation runner for the framework tests.</p>
<pre class="prettify">
&lt;uses-permission android:name="android.permission.RUN_INSTRUMENTATION" /&gt;
&lt;instrumentation android:name="android.tests.FrameworkInstrumentationTestRunner"
android:targetPackage="com.google.android.frameworktest"
android:label="framework instrumentation test runner" /&gt;

View File

@@ -0,0 +1,741 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<base target="_top">
<style type="text/css">
/* default css */
table {
font-size: 1em;
line-height: inherit;
}
tr {
text-align: left;
}
div, address, ol, ul, li, option, select {
margin-top: 0px;
margin-bottom: 0px;
}
p {
margin: 0px;
}
body {
margin: 6px;
padding: 0px;
font-family: Verdana, sans-serif;
font-size: 10pt;
background-color: #ffffff;
}
img {
-moz-force-broken-image-icon: 1;
}
@media screen {
html.pageview {
background-color: #f3f3f3 !important;
}
body {
min-height: 1100px;
counter-reset: __goog_page__;
}
* html body {
height: 1100px;
}
.pageview body {
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
border-right: 2px solid #bbb;
border-bottom: 2px solid #bbb;
width: 648px !important;
margin: 15px auto 25px;
padding: 40px 50px;
}
/* IE6 */
* html {
overflow-y: scroll;
}
* html.pageview body {
overflow-x: auto;
}
/* Prevent repaint errors when scrolling in Safari. This "Star-7" css hack
targets Safari 3.1, but not WebKit nightlies and presumably Safari 4.
That's OK because this bug is fixed in WebKit nightlies/Safari 4 :-). */
html*#wys_frame::before {
content: '\A0';
position: fixed;
overflow: hidden;
width: 0;
height: 0;
top: 0;
left: 0;
}
.writely-callout-data {
display: none;
*display: inline-block;
*width: 0;
*height: 0;
*overflow: hidden;
}
.writely-footnote-marker {
background-image: url('MISSING');
background-color: transparent;
background-repeat: no-repeat;
width: 7px;
overflow: hidden;
height: 16px;
vertical-align: top;
-moz-user-select: none;
}
.editor .writely-footnote-marker {
cursor: move;
}
.writely-footnote-marker-highlight {
background-position: -15px 0;
-moz-user-select: text;
}
.writely-footnote-hide-selection ::-moz-selection, .writely-footnote-hide-selection::-moz-selection {
background: transparent;
}
.writely-footnote-hide-selection ::selection, .writely-footnote-hide-selection::selection {
background: transparent;
}
.writely-footnote-hide-selection {
cursor: move;
}
.editor .writely-comment-yellow {
background-color: #FF9;
background-position: -240px 0;
}
.editor .writely-comment-yellow-hover {
background-color: #FF0;
background-position: -224px 0;
}
.editor .writely-comment-blue {
background-color: #C0D3FF;
background-position: -16px 0;
}
.editor .writely-comment-blue-hover {
background-color: #6292FE;
background-position: 0 0;
}
.editor .writely-comment-orange {
background-color: #FFDEAD;
background-position: -80px 0;
}
.editor .writely-comment-orange-hover {
background-color: #F90;
background-position: -64px 0;
}
.editor .writely-comment-green {
background-color: #99FBB3;
background-position: -48px 0;
}
.editor .writely-comment-green-hover {
background-color: #00F442;
background-position: -32px 0;
}
.editor .writely-comment-cyan {
background-color: #CFF;
background-position: -208px 0;
}
.editor .writely-comment-cyan-hover {
background-color: #0FF;
background-position: -192px 0;
}
.editor .writely-comment-purple {
background-color: #EBCCFF;
background-position: -144px 0;
}
.editor .writely-comment-purple-hover {
background-color: #90F;
background-position: -128px 0;
}
.editor .writely-comment-magenta {
background-color: #FCF;
background-position: -112px 0;
}
.editor .writely-comment-magenta-hover {
background-color: #F0F;
background-position: -96px 0;
}
.editor .writely-comment-red {
background-color: #FFCACA;
background-position: -176px 0;
}
.editor .writely-comment-red-hover {
background-color: #FF7A7A;
background-position: -160px 0;
}
.editor .writely-comment-marker {
background-image: url('MISSING');
background-color: transparent;
padding-right: 11px;
background-repeat: no-repeat;
width: 16px;
height: 16px;
-moz-user-select: none;
}
.editor .writely-comment-hidden {
padding: 0;
background: none;
}
.editor .writely-comment-marker-hidden {
background: none;
padding: 0;
width: 0;
}
.editor .writely-comment-none {
opacity: .2;
filter:progid:DXImageTransform.Microsoft.Alpha(opacity=20);
-moz-opacity: .2;
}
.editor .writely-comment-none-hover {
opacity: .2;
filter:progid:DXImageTransform.Microsoft.Alpha(opacity=20);
-moz-opacity: .2;
}
.br_fix br:not(:-moz-last-node):not(:-moz-first-node) {
position:relative;
left: -1ex
}
.br_fix br+br {
position: static !important
}
}
h6 { font-size: 8pt }
h5 { font-size: 8pt }
h4 { font-size: 10pt }
h3 { font-size: 12pt }
h2 { font-size: 14pt }
h1 { font-size: 18pt }
blockquote {padding: 10px; border: 1px #DDD dashed }
a img {border: 0}
.pb {
border-width: 0;
page-break-after: always;
/* We don't want this to be resizeable, so enforce a width and height
using !important */
height: 1px !important;
width: 100% !important;
}
.editor .pb {
border-top: 1px dashed #C0C0C0;
border-bottom: 1px dashed #C0C0C0;
}
div.google_header, div.google_footer {
position: relative;
margin-top: 1em;
margin-bottom: 1em;
}
/* Table of contents */
.editor div.writely-toc {
background-color: #f3f3f3;
border: 1px solid #ccc;
}
.writely-toc > ol {
padding-left: 3em;
font-weight: bold;
}
ol.writely-toc-subheading {
padding-left: 1em;
font-weight: normal;
}
/* IE6 only */
* html writely-toc ol {
list-style-position: inside;
}
.writely-toc-none {
list-style-type: none;
}
.writely-toc-decimal {
list-style-type: decimal;
}
.writely-toc-upper-alpha {
list-style-type: upper-alpha;
}
.writely-toc-lower-alpha {
list-style-type: lower-alpha;
}
.writely-toc-upper-roman {
list-style-type: upper-roman;
}
.writely-toc-lower-roman {
list-style-type: lower-roman;
}
.writely-toc-disc {
list-style-type: disc;
}
/* end default css */
/* default print css */
@media print {
body {
padding: 0;
margin: 0;
}
div.google_header, div.google_footer {
display: block;
min-height: 0;
border: none;
}
div.google_header {
flow: static(header);
}
/* used to insert page numbers */
div.google_header::before, div.google_footer::before {
position: absolute;
top: 0;
}
div.google_footer {
flow: static(footer);
}
/* always consider this element at the start of the doc */
div#google_footer {
flow: static(footer, start);
}
span.google_pagenumber {
content: counter(page);
}
span.google_pagecount {
content: counter(pages);
}
callout.google_footnote {
display: prince-footnote;
footnote-style-position: inside;
/* These styles keep the footnote from taking on the style of the text
surrounding the footnote marker. They can be overridden in the
document CSS. */
color: #000;
font-family: Verdana;
font-size: 10.0pt;
font-weight: normal;
}
/* Table of contents */
#WritelyTableOfContents a::after {
content: leader('.') target-counter(attr(href), page);
}
#WritelyTableOfContents a {
text-decoration: none;
color: black;
}
}
@page {
@top {
content: flow(header);
}
@bottom {
content: flow(footer);
}
@footnotes {
border-top: solid black thin;
padding-top: 8pt;
}
}
/* end default print css */
/* custom css */
/* end custom css */
/* ui edited css */
body {
font-family: Verdana;
font-size: 10.0pt;
line-height: normal;
background-color: #ffffff;
}
/* end ui edited css */
/* editor CSS */
.editor a:visited {color: #551A8B}
.editor table.zeroBorder {border: 1px dotted gray}
.editor table.zeroBorder td {border: 1px dotted gray}
.editor table.zeroBorder th {border: 1px dotted gray}
.editor div.google_header, .editor div.google_footer {
border: 2px #DDDDDD dashed;
position: static;
width: 100%;
min-height: 2em;
}
.editor .misspell {background-color: yellow}
.editor .writely-comment {
font-size: 9pt;
line-height: 1.4;
padding: 1px;
border: 1px dashed #C0C0C0
}
/* end editor CSS */
</style>
</head>
<body onload="DoPageLoad();"
revision="cfnx2f69_111dp3jzfgb:107">
<h1>
Using the Android Native Development Kit (NDK)
</h1>
version 1.3<br>
<br>
<h2>
Introduction
</h2>
The Android Native Development Kit enables developers to write shared libraries
in C or C++ and call them from Java code. The native shared libraries can be
packaged into apk files along with a normal Android application written in Java,
so that the resulting Android application can be downloaded and installed on an
Android phone.<br>
<br>
The Native Development Kit consists of:<br>
<ul>
<li>
C/C++ headers for native APIs<br>
</li>
<li>
C/C++ libraries for native APIs<br>
</li>
<li>
Documentation
</li>
<li>
Sample Code
</li>
</ul>
<br>
The Native Development Kit is designed to be used with the Android SDK:<br>
<ul>
<li>
The NDK is used to create a shared library containing native code.
</li>
<li>
The SDK is used to create an Android application written in Java that calls
into the native code shared library.
</li>
</ul>
<h1>
</h1>
<h2>
Setting up your machine<br>
</h2>
The Native Development Kit may be installed on either Linux or OS X. Developing
under Windows is not yet supported.<br>
<div>
<h3>
Linux Installation
</h3>
The
Android&nbsp;build&nbsp;is&nbsp;routinely&nbsp;tested&nbsp;on&nbsp;recent&nbsp;versions&nbsp;of&nbsp;Ubuntu&nbsp;(6.06&nbsp;and&nbsp;later),&nbsp;but
may work on other distributions as well.<br>
<h4>
<a name=TOC-Ubuntu-Linux-i386-></a><span style=FONT-FAMILY:Verdana>Ubuntu
Linux (i386)</span>
</h4>
<div style=FONT-FAMILY:Verdana>
To set up your Linux development environment, make sure you have the
following:<span style="WORD-SPACING:0px; FONT-STYLE:normal; FONT-VARIANT:normal; FONT-WEIGHT:normal; font-size-adjust:none; font-stretch:normal; TEXT-TRANSFORM:none; COLOR:#000000; WHITE-SPACE:normal; LETTER-SPACING:normal; border-collapse:separate"><font size=2>
</font></span>
</div>
<div style="MARGIN-TOP:0px; MARGIN-BOTTOM:0px; FONT-FAMILY:Verdana">
<div style="MARGIN-TOP:0px; MARGIN-BOTTOM:0px">
<div style="MARGIN-TOP:0px; MARGIN-BOTTOM:0px">
<ul style="MARGIN-TOP:0px; MARGIN-BOTTOM:0px">
<li style="MARGIN-TOP:8px; MARGIN-BOTTOM:8px">
Git 1.5.4 or
newer<span style="FONT-WEIGHT:normal; WORD-SPACING:0px; TEXT-TRANSFORM:none; COLOR:#000000; FONT-STYLE:normal; WHITE-SPACE:normal; LETTER-SPACING:normal; border-collapse:separate; FONT-VARIANT:normal"><font size=2>.&nbsp;</font></span>
</li>
</ul>
</div>
</div>
</div>
<blockquote style="BORDER:medium none ; MARGIN:0pt 0pt 0pt 40px; PADDING:0px">
<span style=FONT-FAMILY:arial><span style="FONT-WEIGHT:normal; WORD-SPACING:0px; TEXT-TRANSFORM:none; COLOR:#000000; FONT-STYLE:normal; WHITE-SPACE:normal; LETTER-SPACING:normal; border-collapse:separate; FONT-VARIANT:normal"><span style="FONT-FAMILY:courier new,monospace">$
</span></span><span style="FONT-FAMILY:courier new,monospace">sudo apt-get
install git-core<br>
</span></span>
</blockquote>
<div>
<div style="MARGIN-TOP:0px; MARGIN-BOTTOM:0px">
<div style="MARGIN-TOP:0px; MARGIN-BOTTOM:0px; FONT-FAMILY:arial,sans-serif">
<div style="MARGIN-TOP:0px; MARGIN-BOTTOM:0px">
<h4>
<a name=TOC-Ubuntu-Linux-amd64-></a><span style=FONT-FAMILY:Verdana>Ubuntu
Linux (amd64)</span>
</h4>
<span style=FONT-FAMILY:Verdana>This has not been as well
tested.</span>
</div>
<div style="MARGIN-TOP:0px; MARGIN-BOTTOM:0px; FONT-FAMILY:Verdana">
<br>
</div>
<div style="MARGIN-TOP:0px; MARGIN-BOTTOM:0px; FONT-FAMILY:Verdana">
The Android build requires a 32-bit build environment:
</div>
<div style="MARGIN-TOP:0px; MARGIN-BOTTOM:0px; FONT-FAMILY:Verdana">
<ul>
<li>
Get the packages as listed above in the i386
instructions:<span style="FONT-WEIGHT:normal; WORD-SPACING:0px; TEXT-TRANSFORM:none; COLOR:#000000; FONT-STYLE:normal; WHITE-SPACE:normal; LETTER-SPACING:normal; border-collapse:separate; FONT-VARIANT:normal">&nbsp;&nbsp;&nbsp;</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<blockquote style="BORDER:medium none ; MARGIN:0pt 0pt 0pt 40px; PADDING:0px">
<span style=FONT-FAMILY:arial><span style="FONT-WEIGHT:normal; WORD-SPACING:0px; TEXT-TRANSFORM:none; COLOR:#000000; FONT-STYLE:normal; WHITE-SPACE:normal; LETTER-SPACING:normal; border-collapse:separate; FONT-VARIANT:normal"><span style="FONT-FAMILY:courier new,monospace">$&nbsp;</span></span><span style="FONT-FAMILY:courier new,monospace">sudo
apt-get install git-core<br>
</span></span>
</blockquote>
<h4>
<a name=TOC-Other-Linux></a>Other Linux
</h4>
<p>
There's
no&nbsp;reason&nbsp;why&nbsp;Android&nbsp;cannot&nbsp;be&nbsp;built&nbsp;on&nbsp;non-Ubuntu&nbsp;systems<span style=FONT-WEIGHT:normal><font size=2>.&nbsp;In&nbsp;general&nbsp;you&nbsp;will&nbsp;need:</font></span>
</p>
<ul>
<li>
Git&nbsp;1.5.4&nbsp;or&nbsp;newer.&nbsp;You&nbsp;can&nbsp;find&nbsp;it&nbsp;at&nbsp;<a href=http://git.or.cz/ rel=nofollow>http://git.or.cz/</a><span style=FONT-FAMILY:arial></span>
</li>
</ul>
<div>
<h3>
Mac OS Installation
</h3>
<ul>
<li>
<span style=FONT-FAMILY:arial,sans-serif>To build the Android files in a
Mac OS environment, you need an Intel/x86 machine. The Android build
system and tools do not support the older PowerPC architecture.</span>
</li>
<li>
<span style=FONT-FAMILY:arial,sans-serif>Android must be built on a
case-sensitive file system.<br>
</span>
</li>
<ul>
<li>
We recommend that you build Android on a partition that has been
formatted with the "Case-sensitive Journaled HFS+" file system:
</li>
<ul>
<li>
A case-sensitive file system is required because the sources contain
files that differ only in case.
</li>
<li>
Journaled systems are more robust. (This is optional, but
recommended.)
</li>
<li>
HFS+ is required to successfully build Mac OS applications such as
the Android Emulator for OS X.
</li>
</ul>
<li>
If you want to avoid partitioning/formatting your hard drive, you can
use a case-sensitive disk image instead.
</li>
<ul>
<li>
To create the image:<br>
<ul>
<li>
launch /Applications/Utilities/Disk Utility
</li>
<li>
select "New Image"
</li>
<li>
size: 8 GB (this will work, but you can choose more if you want
to)
</li>
<li>
volume format: case sensitive, journaled
</li>
</ul>
</li>
<li>
This will create a .dmg file which, once mounted, acts as a drive
with the required formatting for Android development. For a disk
image named "android.dmg" stored in your home directory, you can add
the following to your ~/.bash_profile to mount the image when you
execute "mountAndroid":<br>
<br>
<div style=MARGIN-LEFT:40px>
<span style="FONT-FAMILY:courier new,monospace"># command to mount
the android file
image</span><br style="FONT-FAMILY:courier new,monospace">
<span style="FONT-FAMILY:courier new,monospace">function
mountAndroid&nbsp; { hdiutil attach ~/android.dmg&nbsp;
-mountpoint /Volumes/android; }</span><br>
</div>
<br>
Once mounted, you'll do all your work in the "android" volume. You
can eject it (unmount it) just like you would with an external
drive.
</li>
</ul>
</ul>
</ul>
<div>
<br>
<ul>
<li>
Install git 1.5.4 or newer. You can find it at
<a href=http://git.or.cz/ rel=nofollow>http://git.or.cz/</a>
</li>
</ul>
<h2>
Installing the Android SDK
</h2>
The Android NDK uses the Android SDK.&nbsp;You can find the Android SDK at
<a href=http://code.google.com/android/download.html id=a.-o title=http://code.google.com/android/download.html>http://code.google.com/android/download.html</a><br>
This version of the Android NDK requires the Cupcake version of the
Android SDK.<br>
<br>
<h2>
Installing the Prebuilt Native Toolchain<br>
</h2>
The NDK uses the prebuilt native toolchain from the Android Open Source
git repository.<br>
<br>
To download the prebuilt native toolchain to your working directory,
execute the following commands:<br>
<br>
<span style="FONT-FAMILY:Courier New"></span>
<div style=MARGIN-LEFT:40px>
<span style="FONT-FAMILY:Courier New">git clone
git://android.git.kernel.org/platform/prebuilt.git</span><br>
<span style="FONT-FAMILY:Courier New">cd prebuilt</span><br>
<span style="FONT-FAMILY:Courier New">git checkout -b cupcake -t
origin/cupcake</span><br>
</div>
<div style=MARGIN-LEFT:40px>
<span style="FONT-FAMILY:Courier New"></span>
</div>
<br>
<h2>
Setting Environment Variables
</h2>
The NDK requires that you set two environment variables:<br>
<ul>
<li>
PREBUILT must be set to the directory that contains the prebuilt
toolchain. Include the "prebuilt" directory in the path. Example:
/Volumes/android/prebuilt<br>
</li>
<li>
ANDROID_SDK_BASE must be set to the directory that contains the
Android SDK. Example: ~/AndroidSDK<br>
</li>
</ul>
<br>
<h2>
<span style=FONT-FAMILY:Verdana>Unpacking the NDK</span>
</h2>
Unpack the android_ndk.tar.gz into your working directory<br>
<br>
<div style=MARGIN-LEFT:40px>
<span style="FONT-FAMILY:Courier New">tar -zxvf
android_ndk.tar.gz</span><br>
</div>
<br>
This will create a directory called ndk. It should contain a README.html
file (this file) and the following directories: config, include, lib, and
sample.<br>
<br>
Look in the "samples" directory for samples showing how to use the NDK.<br>
<br>
<br>
</div>
<br>
</div>
<br>
</div>
<br></body>
</html>

View File

@@ -100,7 +100,7 @@ ndk_src_dest_dir := $(ndk_src_tree)/ndk
bionic_src_dest_dir := $(ndk_src_dest_dir)/include/bionic
# Destinations of all common files (not picked up by tree rules below)
ndk_common_dest_files := $(ndk_common_dest_dir)/README \
ndk_common_dest_files := $(ndk_common_dest_dir)/Android_NDK_README.html \
$(ndk_common_dest_dir)/config/armelf.x \
$(ndk_common_dest_dir)/config/armelflib.x \
$(ndk_common_dest_dir)/lib/crtbegin_dynamic.o \
@@ -114,8 +114,8 @@ ndk_common_full_dest_files := \
$(ndk_common_full_dest_dir)/lib/libstdc++.so
# Install common files outside common trees
$(ndk_common_dest_dir)/README: $(LOCAL_PATH)/README | $(ACP)
@echo "NDK README: from $? to $@"
$(ndk_common_dest_dir)/Android_NDK_README.html: $(LOCAL_PATH)/Android_NDK_README.html | $(ACP)
@echo "NDK Android_NDK_README.html: from $? to $@"
$(copy-file-to-target)
$(ndk_common_dest_dir)/config/armelf.x: $(BUILD_SYSTEM)/armelf.x | $(ACP)

View File

@@ -1,30 +0,0 @@
Using the Native Development Kit (NDK)
version 1.2
PRECONDITIONS
The Native Development Kit may be installed on either Linux or OS X.
The NDK must be installed on a case-sensitive file system. Linux
file systems are always case-sensitive, but the default OS X file
system is case-insenstive. A case-sensitive file sytem can be
created either by partitioning your drive to include a case-sensitive
partition or by creating a disk image that is case-sensitive.
STEP 1
Installing arm-eabi-gcc
-----------------------
1) Untar the android_ndk.tar.gz:
tar -zxvf android_ndk.tar.gz
This will create a directory called ndk. It should include a README file (this
file) and the following directories: config, include, lib, sample and toolchain.
STEP 2
Samples
-------
Look in the "samples" directory for samples of how to use the NDK.

View File

@@ -152,6 +152,18 @@
</intent-filter>
</activity>
<activity android:name=".app.ReorderOnLaunch"
android:label="@string/activity_reorder">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.SAMPLE_CODE" />
</intent-filter>
</activity>
<activity android:name=".app.ReorderTwo" />
<activity android:name=".app.ReorderThree" />
<activity android:name=".app.ReorderFour" />
<!-- Intent Samples -->
<activity android:name=".app.Intents" android:label="@string/activity_intents">

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<!-- Demonstrates using Intent.FLAG_ACTIVITY_REORDER_TO_FRONT.
See corresponding Java code com.example.android.apis.app.ReorderOnLaunch.java. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="4dip"
android:text="@string/reorder_four_text"/>
<Button android:id="@+id/reorder_second_to_front"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/reorder_second_to_front">
</Button>
</LinearLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<!-- Demonstrates using Intent.FLAG_ACTIVITY_REORDER_TO_FRONT.
See corresponding Java code com.android.sdk.app.ReorderOnLaunch.java. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="4dip"
android:text="@string/reorder_on_launch"/>
<Button android:id="@+id/reorder_launch_two"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/reorder_launch_two">
</Button>
</LinearLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<!-- Demonstrates using Intent.FLAG_ACTIVITY_REORDER_TO_FRONT.
See corresponding Java code com.example.android.apis.app.ReorderOnLaunch.java. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="4dip"
android:text="@string/reorder_three_text"/>
<Button android:id="@+id/reorder_launch_four"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/reorder_launch_four">
</Button>
</LinearLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 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.
-->
<!-- Demonstrates using Intent.FLAG_ACTIVITY_REORDER_TO_FRONT.
See corresponding Java code com.example.android.apis.app.ReorderOnLaunch.java. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="4dip"
android:text="@string/reorder_two_text"/>
<Button android:id="@+id/reorder_launch_three"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/reorder_launch_three">
</Button>
</LinearLayout>

View File

@@ -185,6 +185,16 @@
<string name="custom_title_left_button">Change Left</string>
<string name="custom_title_right_button">Change Right</string>
<string name="activity_reorder">App/Activity/Reorder Activities</string>
<string name="reorder_on_launch">This is the first of a sequence of four Activities. A button on the fourth will use the Intent.FLAG_ACTIVITY_REORDER_TO_FRONT flag to bring the second of the activities to the front of the history stack. After that, proceeding back through the history should begin with the newly-frontmost second reorder activity, then the fourth, the third, and finally the first.</string>
<string name="reorder_launch_two">Go to the second</string>
<string name="reorder_two_text">This is the second in a sequence of four Activities.</string>
<string name="reorder_launch_three">Go to the third</string>
<string name="reorder_three_text">This is the third of a sequence of four Activities.</string>
<string name="reorder_launch_four">Go to the fourth</string>
<string name="reorder_four_text">This is the last in a sequence of four Activities.</string>
<string name="reorder_second_to_front">Bring the second in front</string>
<string name="menu_from_xml_title">App/Menu/Inflate from XML</string>
<string name="menu_from_xml_instructions_press_menu">Select a menu resource and press the menu key.</string>
<string name="menu_from_xml_instructions_go_back">If you want to choose another menu resource, go back and re-run this activity.</string>

View File

@@ -22,6 +22,10 @@
android:hint="@string/search_hint"
android:searchMode="showSearchLabelAsBadge"
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"
android:voiceLanguageModel="free_form"
android:voicePromptText="@string/search_invoke"
android:searchSuggestAuthority="com.example.android.apis.SuggestionProvider"
android:searchSuggestSelection=" ? "
/>

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2009 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.example.android.apis.app;
import com.example.android.apis.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class ReorderFour extends Activity {
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.reorder_four);
Button twoButton = (Button) findViewById(R.id.reorder_second_to_front);
twoButton.setOnClickListener(mClickListener);
}
private final OnClickListener mClickListener = new OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(ReorderFour.this, ReorderTwo.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
}
};
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2009 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.example.android.apis.app;
import com.example.android.apis.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class ReorderOnLaunch extends Activity {
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.reorder_on_launch);
Button twoButton = (Button) findViewById(R.id.reorder_launch_two);
twoButton.setOnClickListener(mClickListener);
}
private final OnClickListener mClickListener = new OnClickListener() {
public void onClick(View v) {
startActivity(new Intent(ReorderOnLaunch.this, ReorderTwo.class));
}
};
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2009 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.example.android.apis.app;
import com.example.android.apis.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class ReorderThree extends Activity {
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.reorder_three);
Button twoButton = (Button) findViewById(R.id.reorder_launch_four);
twoButton.setOnClickListener(mClickListener);
}
private final OnClickListener mClickListener = new OnClickListener() {
public void onClick(View v) {
startActivity(new Intent(ReorderThree.this, ReorderFour.class));
}
};
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2009 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.example.android.apis.app;
import com.example.android.apis.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class ReorderTwo extends Activity {
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.reorder_two);
Button twoButton = (Button) findViewById(R.id.reorder_launch_three);
twoButton.setOnClickListener(mClickListener);
}
private final OnClickListener mClickListener = new OnClickListener() {
public void onClick(View v) {
startActivity(new Intent(ReorderTwo.this, ReorderThree.class));
}
};
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.samples.app;
package com.example.android.apis.app;
import android.app.Activity;
import android.content.Intent;

View File

@@ -24,6 +24,7 @@ import android.graphics.drawable.Drawable;
public class ProxyDrawable extends Drawable {
private Drawable mProxy;
private boolean mMutated;
public ProxyDrawable(Drawable target) {
mProxy = target;
@@ -88,5 +89,14 @@ public class ProxyDrawable extends Drawable {
mProxy.setAlpha(alpha);
}
}
@Override
public Drawable mutate() {
if (mProxy != null && !mMutated && super.mutate() == this) {
mProxy.mutate();
mMutated = true;
}
return this;
}
}

View File

@@ -223,14 +223,18 @@ class GTView extends SurfaceView implements SurfaceHolder.Callback {
private float mMotionStartTiltAngle;
private int mMotionDirection;
private boolean mPaused = true;
private boolean mHaveSurface = false;
private boolean mStartAnimating = false;
public void surfaceCreated(SurfaceHolder holder) {
EGL10 egl = (EGL10)EGLContext.getEGL();
mEGLSurface = egl.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, this, null);
egl.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
mHaveSurface = true;
startEGL();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// nothing to do
mHaveSurface = false;
stopEGL();
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
@@ -249,10 +253,26 @@ class GTView extends SurfaceView implements SurfaceHolder.Callback {
getHolder().addCallback(this);
getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU);
AssetManager am = context.getAssets();
startTime = System.currentTimeMillis();
mClock = new Clock();
startEGL();
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
}
/**
* Creates an egl context. If the state of the activity is right, also
* creates the egl surface. Otherwise the surface will be created in a
* future call to createEGLSurface().
*/
private void startEGL() {
EGL10 egl = (EGL10)EGLContext.getEGL();
if (mEGLContext == null) {
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int[] version = new int[2];
egl.eglInitialize(dpy, version);
@@ -264,15 +284,12 @@ class GTView extends SurfaceView implements SurfaceHolder.Callback {
int[] num_config = new int[1];
egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config);
mEGLConfig = configs[0];
mEGLContext = egl.eglCreateContext(dpy, mEGLConfig, EGL10.EGL_NO_CONTEXT, null);
mEGLContext = egl.eglCreateContext(dpy, mEGLConfig,
EGL10.EGL_NO_CONTEXT, null);
mEGLDisplay = dpy;
mClock = new Clock();
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
AssetManager am = mContext.getAssets();
try {
loadAssets(am);
} catch (IOException ioe) {
@@ -284,25 +301,67 @@ class GTView extends SurfaceView implements SurfaceHolder.Callback {
}
}
if (mEGLSurface == null && !mPaused && mHaveSurface) {
mEGLSurface = egl.eglCreateWindowSurface(mEGLDisplay, mEGLConfig,
this, null);
egl.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface,
mEGLContext);
mInitialized = false;
if (mStartAnimating) {
startAnimating();
mStartAnimating = false;
}
}
}
/**
* Destroy the view.
* Destroys the egl context. If an egl surface has been created, it is
* destroyed as well.
*/
public void destroy() {
private void stopEGL() {
EGL10 egl = (EGL10)EGLContext.getEGL();
if (mEGLSurface != null) {
egl.eglMakeCurrent(mEGLDisplay,
egl.EGL_NO_SURFACE, egl.EGL_NO_SURFACE, egl.EGL_NO_CONTEXT);
egl.eglDestroyContext(mEGLDisplay, mEGLContext);
egl.eglDestroySurface(mEGLDisplay, mEGLSurface);
mEGLSurface = null;
}
if (mEGLContext != null) {
egl.eglDestroyContext(mEGLDisplay, mEGLContext);
egl.eglTerminate(mEGLDisplay);
mEGLContext = null;
mEGLDisplay = null;
mEGLConfig = null;
}
}
public void onPause() {
mPaused = true;
stopAnimating();
stopEGL();
}
public void onResume() {
mPaused = false;
startEGL();
}
public void destroy() {
stopAnimating();
stopEGL();
}
/**
* Begin animation.
*/
public void startAnimating() {
if (mEGLSurface == null) {
mStartAnimating = true; // will start when egl surface is created
} else {
mHandler.sendEmptyMessage(INVALIDATE);
}
}
/**
* Quit animation.
@@ -1390,32 +1449,25 @@ public class GlobalTime extends Activity {
GTView gtView = null;
private void setGTView() {
if (gtView == null) {
gtView = new GTView(this);
setContentView(gtView);
}
}
@Override protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
setGTView();
gtView = new GTView(this);
setContentView(gtView);
}
@Override protected void onResume() {
super.onResume();
setGTView();
gtView.onResume();
Looper.myQueue().addIdleHandler(new Idler());
}
@Override protected void onPause() {
super.onPause();
gtView.stopAnimating();
gtView.onPause();
}
@Override protected void onStop() {
super.onStop();
gtView.stopAnimating();
gtView.destroy();
gtView = null;
}

View File

@@ -19,5 +19,5 @@
-->
<resources>
<dimen name="key_height">46px</dimen>
<dimen name="key_height">46dip</dimen>
</resources>

View File

@@ -19,7 +19,7 @@
-->
<resources>
<dimen name="key_height">50px</dimen>
<dimen name="key_height">50dip</dimen>
<dimen name="candidate_font_height">16sp</dimen>
<dimen name="candidate_vertical_padding">6sp</dimen>
</resources>

View File

@@ -27,6 +27,7 @@ import android.view.View;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import java.util.ArrayList;
import java.util.List;
@@ -73,11 +74,19 @@ public class SoftKeyboard extends InputMethodService
private String mWordSeparators;
/**
* Helper function to generate the various keyboard layouts used by the
* input method. Takes care of regenerating the layouts if the width
* of the input method changes.
* Main initialization of the input method component. Be sure to call
* to super class.
*/
private void makeKeyboards() {
@Override public void onCreate() {
super.onCreate();
mWordSeparators = getResources().getString(R.string.word_separators);
}
/**
* This is the point where you can do all of your UI initialization. It
* is called after creation and any configuration change.
*/
@Override public void onInitializeInterface() {
if (mQwertyKeyboard != null) {
// Configuration changes can happen after the keyboard gets recreated,
// so we need to be able to re-build the keyboards if the available
@@ -91,16 +100,6 @@ public class SoftKeyboard extends InputMethodService
mSymbolsShiftedKeyboard = new LatinKeyboard(this, R.xml.symbols_shift);
}
/**
* Main initialization of the input method component. Be sure to call
* to super class.
*/
@Override public void onCreate() {
super.onCreate();
makeKeyboards();
mWordSeparators = getResources().getString(R.string.word_separators);
}
/**
* Called by the framework when your view for creating input needs to
* be generated. This will be called the first time your input method
@@ -108,9 +107,6 @@ public class SoftKeyboard extends InputMethodService
* a configuration change.
*/
@Override public View onCreateInputView() {
// We call makeKeyboards() here to regenerate them if needed due to
// a configuration change.
makeKeyboards();
mInputView = (KeyboardView) getLayoutInflater().inflate(
R.layout.input, null);
mInputView.setOnKeyboardActionListener(this);

View File

@@ -132,7 +132,7 @@ void PropertyServer::SetDefaultProperties(void)
{ "dalvik.vm.stack-trace-file", "/data/anr/traces.txt" },
//{ "dalvik.vm.execution-mode", "int:portable" },
{ "dalvik.vm.enableassertions", "all" }, // -ea
{ "dalvik.vm.verify-bytecode", "false" }, // -Xverify
{ "dalvik.vm.dexopt-flags", "" }, // e.g. "v=a,o=v,m=n"
{ "dalvik.vm.deadlock-predict", "off" }, // -Xdeadlockpredict
//{ "dalvik.vm.jniopts", "forcecopy" }, // -Xjniopts
{ "log.redirect-stdio", "false" }, // -Xlog-stdio

View File

@@ -1,7 +1,7 @@
/*
* Copyright 2007 The Android Open Source Project
*
* Magic entries in /sys/android_power/.
* Magic entries in /sys/class/power_supply/.
*/
#include "Common.h"
@@ -12,30 +12,6 @@
#include <fcntl.h>
#include <sys/ioctl.h>
#if 0
/*
* Set of entries found in /sys/android_power.
*/
typedef enum DeviceIndex {
kPowerUnknown = 0,
kPowerAutoOffTimeout,
kPowerBatteryLevel,
kPowerBatteryLevelLow,
kPowerBatteryLevelRaw,
kPowerBatteryLevelScale,
kPowerBatteryLowLevel,
kPowerBatteryShutdownLevel,
kPowerChargingState,
kPowerRequestState,
kPowerState,
kPowerAcquireFullWakeLock,
kPowerAcquirePartialWakeLock,
kPowerReleaseWakeLock,
} DeviceIndex;
#endif
/*
* Map filename to device index.
*
@@ -47,38 +23,26 @@ static const struct {
//DeviceIndex idx;
const char* data;
} gDeviceMap[] = {
{ "auto_off_timeout", //kPowerAutoOffTimeout,
"\n" },
{ "battery_level", //kPowerBatteryLevel,
"9\n" },
{ "battery_level_low", //kPowerBatteryLevelLow,
{ "ac/online",
"0\n" },
{ "battery_level_raw", //kPowerBatteryLevelRaw,
{ "battery/batt_temp",
"281\n", },
{ "battery/batt_vol",
"4170\n" },
{ "battery/capacity",
"100\n" },
{ "battery_level_scale", //kPowerBatteryLevelScale,
"9\n" },
{ "battery_low_level", //kPowerBatteryLowLevel,
"10\n" },
{ "battery_shutdown_level", //kPowerBatteryShutdownLevel,
"5\n", },
{ "charging_state", //kPowerChargingState,
"Maintaining\n" },
{ "request_state", //kPowerRequestState,
"wake\n" },
{ "state", //kPowerState,
"0-1-0\n" },
{ "acquire_full_wake_lock", //kPowerAcquireFullWakeLock,
"\n" },
{ "acquire_partial_wake_lock", //kPowerAcquirePartialWakeLock,
"\n" },
{ "release_wake_lock", //kPowerReleaseWakeLock,
"radio-interface PowerManagerService KeyEvents\n" },
{ "wait_for_fb_sleep", //kSleepFileName,
"" }, // this means "block forever on read"
{ "wait_for_fb_wake", //kWakeFileName,
"0" },
{ "battery/health",
"Good\n" },
{ "battery/present",
"0\n" },
{ "battery/status",
"Full" },
{ "battery/technology",
"Li-ion\n" },
{ "usb/online",
"1\n" },
};
/*
@@ -96,7 +60,7 @@ typedef struct PowerState {
*/
static void configureInitialState(const char* pathName, PowerState* powerState)
{
const char* cp = pathName + strlen("/sys/android_power/");
const char* cp = pathName + strlen("/sys/class/power_supply/");
int i;
powerState->which = -1;
@@ -134,8 +98,11 @@ static ssize_t readPower(FakeDev* dev, int fd, void* buf, size_t count)
wsLog("%s: read %d\n", dev->debugName, count);
if (state->which < 0 || state->which >= sizeof(gDeviceMap)/sizeof(gDeviceMap[0]))
if (state->which < 0 ||
state->which >= (int) (sizeof(gDeviceMap)/sizeof(gDeviceMap[0])))
{
return 0;
}
const char* data = gDeviceMap[state->which].data;
size_t strLen = strlen(data);

View File

@@ -47,10 +47,6 @@ resources.)
* Devices we intercept.
*
* Needed:
* /sys/android_power/battery_level_scale
* /sys/android_power/battery_level
* /sys/android_power/battery_level_raw
* /sys/android_power/charging_state
* /dev/alarm
* radio
*/
@@ -70,7 +66,7 @@ FakedPath fakedpaths[] =
{ "/dev/input/event0", wsOpenDevEvent },
{ "/dev/input/*", NULL },
{ "/dev/log/*", wsOpenDevLog },
{ "/sys/android_power/*", wsOpenDevPower },
{ "/sys/class/power_supply/*", wsOpenDevPower },
{ "/sys/devices/platform/android-vibrator/enable", wsOpenDevVibrator },
{ "/sys/qemu_trace/*", NULL },
{ NULL, NULL }

View File

@@ -32,9 +32,9 @@ public final class AndroidLocation {
private static final String ANDROID_SDK_VERSION = "SDK-1.0";
/**
* VM folder inside the path returned by {@link #getFolder()}
* Virtual Device folder inside the path returned by {@link #getFolder()}
*/
public static final String FOLDER_VMS = "vm";
public static final String FOLDER_AVD = "avd";
/**
* Throw when the location of the android folder couldn't be found.
@@ -56,7 +56,7 @@ public final class AndroidLocation {
*/
public final static String getFolder() throws AndroidLocationException {
if (sPrefsLocation == null) {
String home = findValidPath("user.home", "HOME");
String home = findValidPath("ANDROID_SDK_HOME", "user.home", "HOME");
// if the above failed, we throw an exception.
if (home == null) {

View File

@@ -319,7 +319,7 @@ public final class ApkBuilder {
* @param files
* @param javaResources
* @param storeType the optional type of the debug keystore. If <code>null</code>, the default
* keystore type of the VM is used.
* keystore type of the Java VM is used.
*/
private void createPackage(File outFile, ArrayList<FileInputStream> zipArchives,
ArrayList<File> files, ArrayList<ApkFile> javaResources,

View File

@@ -268,7 +268,9 @@ final class AdbHelper {
};
byte[] reply;
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to talk
@@ -315,8 +317,11 @@ final class AdbHelper {
return null;
}
imageParams.data = reply;
} finally {
if (adbChan != null) {
adbChan.close();
}
}
return imageParams;
}
@@ -330,10 +335,13 @@ final class AdbHelper {
throws IOException {
Log.v("ddms", "execute: running " + command);
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to talk
// if the device is not -1, then we first tell adb we're looking to
// talk
// to a specific device
setDevice(adbChan, device);
@@ -343,8 +351,7 @@ final class AdbHelper {
AdbResponse resp = readAdbResponse(adbChan, false /* readDiagString */);
if (!resp.ioSuccess || !resp.okay) {
Log.e("ddms", "ADB rejected shell command (" + command + "): "
+ resp.message);
Log.e("ddms", "ADB rejected shell command (" + command + "): " + resp.message);
throw new IOException("sad result from adb: " + resp.message);
}
@@ -362,8 +369,8 @@ final class AdbHelper {
if (count < 0) {
// we're at the end, we flush the output
rcvr.flush();
Log.v("ddms",
"execute '" + command + "' on '" + device + "' : EOF hit. Read: " + count);
Log.v("ddms", "execute '" + command + "' on '" + device + "' : EOF hit. Read: "
+ count);
break;
} else if (count == 0) {
try {
@@ -372,17 +379,18 @@ final class AdbHelper {
}
} else {
if (rcvr != null) {
rcvr.addOutput(buf.array(), buf.arrayOffset(), buf
.position());
rcvr.addOutput(buf.array(), buf.arrayOffset(), buf.position());
}
buf.rewind();
}
}
} finally {
if (adbChan != null) {
adbChan.close();
}
Log.v("ddms", "execute: returning");
}
}
/**
* Runs the Event log service on the {@link Device}, and provides its output to the
@@ -407,7 +415,10 @@ final class AdbHelper {
*/
public static void runLogService(InetSocketAddress adbSockAddr, Device device, String logName,
LogReceiver rcvr) throws IOException {
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
// if the device is not -1, then we first tell adb we're looking to talk
@@ -448,9 +459,12 @@ final class AdbHelper {
buf.rewind();
}
}
} finally {
if (adbChan != null) {
adbChan.close();
}
}
}
/**
* Creates a port forwarding between a local and a remote port.
@@ -464,7 +478,9 @@ final class AdbHelper {
public static boolean createForward(InetSocketAddress adbSockAddr, Device device, int localPort,
int remotePort) throws IOException {
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
byte[] request = formAdbRequest(String.format(
@@ -479,8 +495,11 @@ final class AdbHelper {
if (!resp.ioSuccess || !resp.okay) {
throw new IOException("Device rejected command: " + resp.message);
}
} finally {
if (adbChan != null) {
adbChan.close();
}
}
return true;
}
@@ -497,7 +516,9 @@ final class AdbHelper {
public static boolean removeForward(InetSocketAddress adbSockAddr, Device device, int localPort,
int remotePort) throws IOException {
SocketChannel adbChan = SocketChannel.open(adbSockAddr);
SocketChannel adbChan = null;
try {
adbChan = SocketChannel.open(adbSockAddr);
adbChan.configureBlocking(false);
byte[] request = formAdbRequest(String.format(
@@ -512,8 +533,11 @@ final class AdbHelper {
if (!resp.ioSuccess || !resp.okay) {
throw new IOException("Device rejected command: " + resp.message);
}
} finally {
if (adbChan != null) {
adbChan.close();
}
}
return true;
}

View File

@@ -69,8 +69,8 @@ public final class Device implements IDevice {
/** Serial number of the device */
String serialNumber = null;
/** Name of the vm */
String mVmName = null;
/** Name of the AVD */
String mAvdName = null;
/** State of the device. */
DeviceState state = null;
@@ -94,8 +94,8 @@ public final class Device implements IDevice {
return serialNumber;
}
public String getVmName() {
return mVmName;
public String getAvdName() {
return mAvdName;
}

View File

@@ -420,11 +420,11 @@ final class DeviceMonitor {
device.executeShellCommand(GetPropReceiver.GETPROP_COMMAND,
new GetPropReceiver(device));
// now get the emulator VM name (if applicable).
// now get the emulator Virtual Device name (if applicable).
if (device.isEmulator()) {
EmulatorConsole console = EmulatorConsole.getConsole(device);
if (console != null) {
device.mVmName = console.getVmName();
device.mAvdName = console.getAvdName();
}
}
} catch (IOException e) {
@@ -470,7 +470,7 @@ final class DeviceMonitor {
} catch (IOException e1) {
// we can ignore that one. It may already have been closed.
}
Log.e("DeviceMonitor",
Log.d("DeviceMonitor",
"Connection Failure when starting to monitor device '"
+ device + "' : " + e.getMessage());
}
@@ -558,7 +558,7 @@ final class DeviceMonitor {
processIncomingJdwpData(device, socket, length);
} catch (IOException ioe) {
Log.e("DeviceMonitor",
Log.d("DeviceMonitor",
"Error reading jdwp list: " + ioe.getMessage());
socket.close();

View File

@@ -54,7 +54,7 @@ public final class EmulatorConsole {
private final static String HOST = "127.0.0.1"; //$NON-NLS-1$
private final static String COMMAND_PING = "help\r\n"; //$NON-NLS-1$
private final static String COMMAND_VM_NAME = "vm name\r\n"; //$NON-NLS-1$
private final static String COMMAND_AVD_NAME = "vm name\r\n"; //$NON-NLS-1$ // TODO change with emulator
private final static String COMMAND_KILL = "kill\r\n"; //$NON-NLS-1$
private final static String COMMAND_GSM_STATUS = "gsm status\r\n"; //$NON-NLS-1$
private final static String COMMAND_GSM_CALL = "gsm call %1$s\r\n"; //$NON-NLS-1$
@@ -309,8 +309,8 @@ public final class EmulatorConsole {
}
}
public synchronized String getVmName() {
if (sendCommand(COMMAND_VM_NAME)) {
public synchronized String getAvdName() {
if (sendCommand(COMMAND_AVD_NAME)) {
String[] result = readLines();
if (result != null && result.length == 2) { // this should be the name on first line,
// and ok on 2nd line

View File

@@ -46,13 +46,13 @@ public interface IDevice {
public String getSerialNumber();
/**
* Returns the name of the VM the emulator is running.
* Returns the name of the AVD the emulator is running.
* <p/>This is only valid if {@link #isEmulator()} returns true.
* <p/>If the emulator is not running any VM (for instance it's running from an Android source
* <p/>If the emulator is not running any AVD (for instance it's running from an Android source
* tree build), this method will return "<code>&lt;build&gt;</code>".
* @return the name of the VM or <code>null</code> if there isn't any.
* @return the name of the AVD or <code>null</code> if there isn't any.
*/
public String getVmName();
public String getAvdName();
/**
* Returns the state of the device.

View File

@@ -201,7 +201,7 @@ public class RemoteAndroidTestRunnerTest extends TestCase {
throw new UnsupportedOperationException();
}
public String getVmName() {
public String getAvdName() {
return "";
}

View File

@@ -200,15 +200,15 @@ public final class DevicePanel extends Panel implements IDebugBridgeChangeListen
case DEVICE_COL_STATE:
return getStateString(device);
case DEVICE_COL_BUILD: {
String vmName = device.getVmName();
String avdName = device.getAvdName();
String debuggable = device.getProperty(Device.PROP_DEBUGGABLE);
String version = device.getProperty(Device.PROP_BUILD_VERSION);
if (device.isEmulator()) {
if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$
return String.format("%1$s [%2$s, debug]", vmName, //$NON-NLS-1$
return String.format("%1$s [%2$s, debug]", avdName, //$NON-NLS-1$
version);
} else {
return String.format("%1$s [%2$s]", vmName, version); //$NON-NLS-1$
return String.format("%1$s [%2$s]", avdName, version); //$NON-NLS-1$
}
} else {
if (debuggable != null && debuggable.equals("1")) { //$NON-NLS-1$

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2008 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.ddmuilib.log.event;
import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventLogParser;
import java.util.ArrayList;
public class DisplayFilteredLog extends DisplayLog {
public DisplayFilteredLog(String name) {
super(name);
}
/**
* Adds event to the display.
*/
@Override
void newEvent(EventContainer event, EventLogParser logParser) {
ArrayList<ValueDisplayDescriptor> valueDescriptors =
new ArrayList<ValueDisplayDescriptor>();
ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors =
new ArrayList<OccurrenceDisplayDescriptor>();
if (filterEvent(event, valueDescriptors, occurrenceDescriptors)) {
addToLog(event, logParser, valueDescriptors, occurrenceDescriptors);
}
}
/**
* Gets display type
*
* @return display type as an integer
*/
@Override
int getDisplayType() {
return DISPLAY_TYPE_FILTERED_LOG;
}
}

View File

@@ -0,0 +1,422 @@
/*
* Copyright (C) 2008 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.ddmuilib.log.event;
import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventLogParser;
import com.android.ddmlib.log.EventValueDescription;
import com.android.ddmlib.log.InvalidTypeException;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
import org.jfree.chart.renderer.xy.XYAreaRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class DisplayGraph extends EventDisplay {
public DisplayGraph(String name) {
super(name);
}
/**
* Resets the display.
*/
@Override
void resetUI() {
Collection<TimeSeriesCollection> datasets = mValueTypeDataSetMap.values();
for (TimeSeriesCollection dataset : datasets) {
dataset.removeAllSeries();
}
if (mOccurrenceDataSet != null) {
mOccurrenceDataSet.removeAllSeries();
}
mValueDescriptorSeriesMap.clear();
mOcurrenceDescriptorSeriesMap.clear();
}
/**
* Creates the UI for the event display.
* @param parent the parent composite.
* @param logParser the current log parser.
* @return the created control (which may have children).
*/
@Override
public Control createComposite(final Composite parent, EventLogParser logParser,
final ILogColumnListener listener) {
String title = getChartTitle(logParser);
return createCompositeChart(parent, logParser, title);
}
/**
* Adds event to the display.
*/
@Override
void newEvent(EventContainer event, EventLogParser logParser) {
ArrayList<ValueDisplayDescriptor> valueDescriptors =
new ArrayList<ValueDisplayDescriptor>();
ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors =
new ArrayList<OccurrenceDisplayDescriptor>();
if (filterEvent(event, valueDescriptors, occurrenceDescriptors)) {
updateChart(event, logParser, valueDescriptors, occurrenceDescriptors);
}
}
/**
* Updates the chart with the {@link EventContainer} by adding the values/occurrences defined
* by the {@link ValueDisplayDescriptor} and {@link OccurrenceDisplayDescriptor} objects from
* the two lists.
* <p/>This method is only called when at least one of the descriptor list is non empty.
* @param event
* @param logParser
* @param valueDescriptors
* @param occurrenceDescriptors
*/
private void updateChart(EventContainer event, EventLogParser logParser,
ArrayList<ValueDisplayDescriptor> valueDescriptors,
ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
Map<Integer, String> tagMap = logParser.getTagMap();
Millisecond millisecondTime = null;
long msec = -1;
// If the event container is a cpu container (tag == 2721), and there is no descriptor
// for the total CPU load, then we do accumulate all the values.
boolean accumulateValues = false;
double accumulatedValue = 0;
if (event.mTag == 2721) {
accumulateValues = true;
for (ValueDisplayDescriptor descriptor : valueDescriptors) {
accumulateValues &= (descriptor.valueIndex != 0);
}
}
for (ValueDisplayDescriptor descriptor : valueDescriptors) {
try {
// get the hashmap for this descriptor
HashMap<Integer, TimeSeries> map = mValueDescriptorSeriesMap.get(descriptor);
// if it's not there yet, we create it.
if (map == null) {
map = new HashMap<Integer, TimeSeries>();
mValueDescriptorSeriesMap.put(descriptor, map);
}
// get the TimeSeries for this pid
TimeSeries timeSeries = map.get(event.pid);
// if it doesn't exist yet, we create it
if (timeSeries == null) {
// get the series name
String seriesFullName = null;
String seriesLabel = getSeriesLabel(event, descriptor);
switch (mValueDescriptorCheck) {
case EVENT_CHECK_SAME_TAG:
seriesFullName = String.format("%1$s / %2$s", seriesLabel,
descriptor.valueName);
break;
case EVENT_CHECK_SAME_VALUE:
seriesFullName = String.format("%1$s", seriesLabel);
break;
default:
seriesFullName = String.format("%1$s / %2$s: %3$s", seriesLabel,
tagMap.get(descriptor.eventTag),
descriptor.valueName);
break;
}
// get the data set for this ValueType
TimeSeriesCollection dataset = getValueDataset(
logParser.getEventInfoMap().get(event.mTag)[descriptor.valueIndex]
.getValueType(),
accumulateValues);
// create the series
timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
if (mMaximumChartItemAge != -1) {
timeSeries.setMaximumItemAge(mMaximumChartItemAge * 1000);
}
dataset.addSeries(timeSeries);
// add it to the map.
map.put(event.pid, timeSeries);
}
// update the timeSeries.
// get the value from the event
double value = event.getValueAsDouble(descriptor.valueIndex);
// accumulate the values if needed.
if (accumulateValues) {
accumulatedValue += value;
value = accumulatedValue;
}
// get the time
if (millisecondTime == null) {
msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
millisecondTime = new Millisecond(new Date(msec));
}
// add the value to the time series
timeSeries.addOrUpdate(millisecondTime, value);
} catch (InvalidTypeException e) {
// just ignore this descriptor if there's a type mismatch
}
}
for (OccurrenceDisplayDescriptor descriptor : occurrenceDescriptors) {
try {
// get the hashmap for this descriptor
HashMap<Integer, TimeSeries> map = mOcurrenceDescriptorSeriesMap.get(descriptor);
// if it's not there yet, we create it.
if (map == null) {
map = new HashMap<Integer, TimeSeries>();
mOcurrenceDescriptorSeriesMap.put(descriptor, map);
}
// get the TimeSeries for this pid
TimeSeries timeSeries = map.get(event.pid);
// if it doesn't exist yet, we create it.
if (timeSeries == null) {
String seriesLabel = getSeriesLabel(event, descriptor);
String seriesFullName = String.format("[%1$s:%2$s]",
tagMap.get(descriptor.eventTag), seriesLabel);
timeSeries = new TimeSeries(seriesFullName, Millisecond.class);
if (mMaximumChartItemAge != -1) {
timeSeries.setMaximumItemAge(mMaximumChartItemAge);
}
getOccurrenceDataSet().addSeries(timeSeries);
map.put(event.pid, timeSeries);
}
// update the series
// get the time
if (millisecondTime == null) {
msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
millisecondTime = new Millisecond(new Date(msec));
}
// add the value to the time series
timeSeries.addOrUpdate(millisecondTime, 0); // the value is unused
} catch (InvalidTypeException e) {
// just ignore this descriptor if there's a type mismatch
}
}
// go through all the series and remove old values.
if (msec != -1 && mMaximumChartItemAge != -1) {
Collection<HashMap<Integer, TimeSeries>> pidMapValues =
mValueDescriptorSeriesMap.values();
for (HashMap<Integer, TimeSeries> pidMapValue : pidMapValues) {
Collection<TimeSeries> seriesCollection = pidMapValue.values();
for (TimeSeries timeSeries : seriesCollection) {
timeSeries.removeAgedItems(msec, true);
}
}
pidMapValues = mOcurrenceDescriptorSeriesMap.values();
for (HashMap<Integer, TimeSeries> pidMapValue : pidMapValues) {
Collection<TimeSeries> seriesCollection = pidMapValue.values();
for (TimeSeries timeSeries : seriesCollection) {
timeSeries.removeAgedItems(msec, true);
}
}
}
}
/**
* Returns a {@link TimeSeriesCollection} for a specific {@link com.android.ddmlib.log.EventValueDescription.ValueType}.
* If the data set is not yet created, it is first allocated and set up into the
* {@link org.jfree.chart.JFreeChart} object.
* @param type the {@link com.android.ddmlib.log.EventValueDescription.ValueType} of the data set.
* @param accumulateValues
*/
private TimeSeriesCollection getValueDataset(EventValueDescription.ValueType type, boolean accumulateValues) {
TimeSeriesCollection dataset = mValueTypeDataSetMap.get(type);
if (dataset == null) {
// create the data set and store it in the map
dataset = new TimeSeriesCollection();
mValueTypeDataSetMap.put(type, dataset);
// create the renderer and configure it depending on the ValueType
AbstractXYItemRenderer renderer;
if (type == EventValueDescription.ValueType.PERCENT && accumulateValues) {
renderer = new XYAreaRenderer();
} else {
XYLineAndShapeRenderer r = new XYLineAndShapeRenderer();
r.setBaseShapesVisible(type != EventValueDescription.ValueType.PERCENT);
renderer = r;
}
// set both the dataset and the renderer in the plot object.
XYPlot xyPlot = mChart.getXYPlot();
xyPlot.setDataset(mDataSetCount, dataset);
xyPlot.setRenderer(mDataSetCount, renderer);
// put a new axis label, and configure it.
NumberAxis axis = new NumberAxis(type.toString());
if (type == EventValueDescription.ValueType.PERCENT) {
// force percent range to be (0,100) fixed.
axis.setAutoRange(false);
axis.setRange(0., 100.);
}
// for the index, we ignore the occurrence dataset
int count = mDataSetCount;
if (mOccurrenceDataSet != null) {
count--;
}
xyPlot.setRangeAxis(count, axis);
if ((count % 2) == 0) {
xyPlot.setRangeAxisLocation(count, AxisLocation.BOTTOM_OR_LEFT);
} else {
xyPlot.setRangeAxisLocation(count, AxisLocation.TOP_OR_RIGHT);
}
// now we link the dataset and the axis
xyPlot.mapDatasetToRangeAxis(mDataSetCount, count);
mDataSetCount++;
}
return dataset;
}
/**
* Return the series label for this event. This only contains the pid information.
* @param event the {@link EventContainer}
* @param descriptor the {@link OccurrenceDisplayDescriptor}
* @return the series label.
* @throws InvalidTypeException
*/
private String getSeriesLabel(EventContainer event, OccurrenceDisplayDescriptor descriptor)
throws InvalidTypeException {
if (descriptor.seriesValueIndex != -1) {
if (descriptor.includePid == false) {
return event.getValueAsString(descriptor.seriesValueIndex);
} else {
return String.format("%1$s (%2$d)",
event.getValueAsString(descriptor.seriesValueIndex), event.pid);
}
}
return Integer.toString(event.pid);
}
/**
* Returns the {@link TimeSeriesCollection} for the occurrence display. If the data set is not
* yet created, it is first allocated and set up into the {@link org.jfree.chart.JFreeChart} object.
*/
private TimeSeriesCollection getOccurrenceDataSet() {
if (mOccurrenceDataSet == null) {
mOccurrenceDataSet = new TimeSeriesCollection();
XYPlot xyPlot = mChart.getXYPlot();
xyPlot.setDataset(mDataSetCount, mOccurrenceDataSet);
OccurrenceRenderer renderer = new OccurrenceRenderer();
renderer.setBaseShapesVisible(false);
xyPlot.setRenderer(mDataSetCount, renderer);
mDataSetCount++;
}
return mOccurrenceDataSet;
}
/**
* Gets display type
*
* @return display type as an integer
*/
@Override
int getDisplayType() {
return DISPLAY_TYPE_GRAPH;
}
/**
* Sets the current {@link EventLogParser} object.
*/
@Override
protected void setNewLogParser(EventLogParser logParser) {
if (mChart != null) {
mChart.setTitle(getChartTitle(logParser));
}
}
/**
* Returns a meaningful chart title based on the value of {@link #mValueDescriptorCheck}.
*
* @param logParser the logParser.
* @return the chart title.
*/
private String getChartTitle(EventLogParser logParser) {
if (mValueDescriptors.size() > 0) {
String chartDesc = null;
switch (mValueDescriptorCheck) {
case EVENT_CHECK_SAME_TAG:
if (logParser != null) {
chartDesc = logParser.getTagMap().get(mValueDescriptors.get(0).eventTag);
}
break;
case EVENT_CHECK_SAME_VALUE:
if (logParser != null) {
chartDesc = String.format("%1$s / %2$s",
logParser.getTagMap().get(mValueDescriptors.get(0).eventTag),
mValueDescriptors.get(0).valueName);
}
break;
}
if (chartDesc != null) {
return String.format("%1$s - %2$s", mName, chartDesc);
}
}
return mName;
}
}

View File

@@ -0,0 +1,379 @@
/*
* Copyright (C) 2008 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.ddmuilib.log.event;
import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventLogParser;
import com.android.ddmlib.log.EventValueDescription;
import com.android.ddmlib.log.InvalidTypeException;
import com.android.ddmuilib.DdmUiPreferences;
import com.android.ddmuilib.TableHelper;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ScrollBar;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import java.util.ArrayList;
import java.util.Calendar;
public class DisplayLog extends EventDisplay {
public DisplayLog(String name) {
super(name);
}
private final static String PREFS_COL_DATE = "EventLogPanel.log.Col1"; //$NON-NLS-1$
private final static String PREFS_COL_PID = "EventLogPanel.log.Col2"; //$NON-NLS-1$
private final static String PREFS_COL_EVENTTAG = "EventLogPanel.log.Col3"; //$NON-NLS-1$
private final static String PREFS_COL_VALUENAME = "EventLogPanel.log.Col4"; //$NON-NLS-1$
private final static String PREFS_COL_VALUE = "EventLogPanel.log.Col5"; //$NON-NLS-1$
private final static String PREFS_COL_TYPE = "EventLogPanel.log.Col6"; //$NON-NLS-1$
/**
* Resets the display.
*/
@Override
void resetUI() {
mLogTable.removeAll();
}
/**
* Adds event to the display.
*/
@Override
void newEvent(EventContainer event, EventLogParser logParser) {
addToLog(event, logParser);
}
/**
* Creates the UI for the event display.
*
* @param parent the parent composite.
* @param logParser the current log parser.
* @return the created control (which may have children).
*/
@Override
Control createComposite(Composite parent, EventLogParser logParser, ILogColumnListener listener) {
return createLogUI(parent, listener);
}
/**
* Adds an {@link EventContainer} to the log.
*
* @param event the event.
* @param logParser the log parser.
*/
private void addToLog(EventContainer event, EventLogParser logParser) {
ScrollBar bar = mLogTable.getVerticalBar();
boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
// get the date.
Calendar c = Calendar.getInstance();
long msec = (long) event.sec * 1000L;
c.setTimeInMillis(msec);
// convert the time into a string
String date = String.format("%1$tF %1$tT", c);
String eventName = logParser.getTagMap().get(event.mTag);
String pidName = Integer.toString(event.pid);
// get the value description
EventValueDescription[] valueDescription = logParser.getEventInfoMap().get(event.mTag);
if (valueDescription != null) {
for (int i = 0; i < valueDescription.length; i++) {
EventValueDescription description = valueDescription[i];
try {
String value = event.getValueAsString(i);
logValue(date, pidName, eventName, description.getName(), value,
description.getEventValueType(), description.getValueType());
} catch (InvalidTypeException e) {
logValue(date, pidName, eventName, description.getName(), e.getMessage(),
description.getEventValueType(), description.getValueType());
}
}
// scroll if needed, by showing the last item
if (scroll) {
int itemCount = mLogTable.getItemCount();
if (itemCount > 0) {
mLogTable.showItem(mLogTable.getItem(itemCount - 1));
}
}
}
}
/**
* Adds an {@link EventContainer} to the log. Only add the values/occurrences defined by
* the list of descriptors. If an event is configured to be displayed by value and occurrence,
* only the values are displayed (as they mark an event occurrence anyway).
* <p/>This method is only called when at least one of the descriptor list is non empty.
*
* @param event
* @param logParser
* @param valueDescriptors
* @param occurrenceDescriptors
*/
protected void addToLog(EventContainer event, EventLogParser logParser,
ArrayList<ValueDisplayDescriptor> valueDescriptors,
ArrayList<OccurrenceDisplayDescriptor> occurrenceDescriptors) {
ScrollBar bar = mLogTable.getVerticalBar();
boolean scroll = bar.getMaximum() == bar.getSelection() + bar.getThumb();
// get the date.
Calendar c = Calendar.getInstance();
long msec = (long) event.sec * 1000L;
c.setTimeInMillis(msec);
// convert the time into a string
String date = String.format("%1$tF %1$tT", c);
String eventName = logParser.getTagMap().get(event.mTag);
String pidName = Integer.toString(event.pid);
if (valueDescriptors.size() > 0) {
for (ValueDisplayDescriptor descriptor : valueDescriptors) {
logDescriptor(event, descriptor, date, pidName, eventName, logParser);
}
} else {
// we display the event. Since the StringBuilder contains the header (date, event name,
// pid) at this point, there isn't anything else to display.
}
// scroll if needed, by showing the last item
if (scroll) {
int itemCount = mLogTable.getItemCount();
if (itemCount > 0) {
mLogTable.showItem(mLogTable.getItem(itemCount - 1));
}
}
}
/**
* Logs a value in the ui.
*
* @param date
* @param pid
* @param event
* @param valueName
* @param value
* @param eventValueType
* @param valueType
*/
private void logValue(String date, String pid, String event, String valueName,
String value, EventContainer.EventValueType eventValueType, EventValueDescription.ValueType valueType) {
TableItem item = new TableItem(mLogTable, SWT.NONE);
item.setText(0, date);
item.setText(1, pid);
item.setText(2, event);
item.setText(3, valueName);
item.setText(4, value);
String type;
if (valueType != EventValueDescription.ValueType.NOT_APPLICABLE) {
type = String.format("%1$s, %2$s", eventValueType.toString(), valueType.toString());
} else {
type = eventValueType.toString();
}
item.setText(5, type);
}
/**
* Logs a value from an {@link EventContainer} as defined by the {@link ValueDisplayDescriptor}.
*
* @param event the EventContainer
* @param descriptor the ValueDisplayDescriptor defining which value to display.
* @param date the date of the event in a string.
* @param pidName
* @param eventName
* @param logParser
*/
private void logDescriptor(EventContainer event, ValueDisplayDescriptor descriptor,
String date, String pidName, String eventName, EventLogParser logParser) {
String value;
try {
value = event.getValueAsString(descriptor.valueIndex);
} catch (InvalidTypeException e) {
value = e.getMessage();
}
EventValueDescription[] values = logParser.getEventInfoMap().get(event.mTag);
EventValueDescription valueDescription = values[descriptor.valueIndex];
logValue(date, pidName, eventName, descriptor.valueName, value,
valueDescription.getEventValueType(), valueDescription.getValueType());
}
/**
* Creates the UI for a log display.
*
* @param parent the parent {@link Composite}
* @param listener the {@link ILogColumnListener} to notify on column resize events.
* @return the top Composite of the UI.
*/
private Control createLogUI(Composite parent, final ILogColumnListener listener) {
Composite mainComp = new Composite(parent, SWT.NONE);
GridLayout gl;
mainComp.setLayout(gl = new GridLayout(1, false));
gl.marginHeight = gl.marginWidth = 0;
mainComp.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
mLogTable = null;
}
});
Label l = new Label(mainComp, SWT.CENTER);
l.setText(mName);
l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mLogTable = new Table(mainComp, SWT.MULTI | SWT.FULL_SELECTION | SWT.V_SCROLL |
SWT.BORDER);
mLogTable.setLayoutData(new GridData(GridData.FILL_BOTH));
IPreferenceStore store = DdmUiPreferences.getStore();
TableColumn col = TableHelper.createTableColumn(
mLogTable, "Time",
SWT.LEFT, "0000-00-00 00:00:00", PREFS_COL_DATE, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(0, (TableColumn) source);
}
}
});
col = TableHelper.createTableColumn(
mLogTable, "pid",
SWT.LEFT, "0000", PREFS_COL_PID, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(1, (TableColumn) source);
}
}
});
col = TableHelper.createTableColumn(
mLogTable, "Event",
SWT.LEFT, "abcdejghijklmno", PREFS_COL_EVENTTAG, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(2, (TableColumn) source);
}
}
});
col = TableHelper.createTableColumn(
mLogTable, "Name",
SWT.LEFT, "Process Name", PREFS_COL_VALUENAME, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(3, (TableColumn) source);
}
}
});
col = TableHelper.createTableColumn(
mLogTable, "Value",
SWT.LEFT, "0000000", PREFS_COL_VALUE, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(4, (TableColumn) source);
}
}
});
col = TableHelper.createTableColumn(
mLogTable, "Type",
SWT.LEFT, "long, seconds", PREFS_COL_TYPE, store); //$NON-NLS-1$
col.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
Object source = e.getSource();
if (source instanceof TableColumn) {
listener.columnResized(5, (TableColumn) source);
}
}
});
mLogTable.setHeaderVisible(true);
mLogTable.setLinesVisible(true);
return mainComp;
}
/**
* Resizes the <code>index</code>-th column of the log {@link Table} (if applicable).
* <p/>
* This does nothing if the <code>Table</code> object is <code>null</code> (because the display
* type does not use a column) or if the <code>index</code>-th column is in fact the originating
* column passed as argument.
*
* @param index the index of the column to resize
* @param sourceColumn the original column that was resize, and on which we need to sync the
* index-th column width.
*/
@Override
void resizeColumn(int index, TableColumn sourceColumn) {
if (mLogTable != null) {
TableColumn col = mLogTable.getColumn(index);
if (col != sourceColumn) {
col.setWidth(sourceColumn.getWidth());
}
}
}
/**
* Gets display type
*
* @return display type as an integer
*/
@Override
int getDisplayType() {
return DISPLAY_TYPE_LOG_ALL;
}
}

View File

@@ -0,0 +1,338 @@
/*
* Copyright (C) 2008 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.ddmuilib.log.event;
import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventLogParser;
import com.android.ddmlib.log.InvalidTypeException;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.jfree.chart.labels.CustomXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.SimpleTimePeriod;
import org.jfree.data.time.TimePeriodValues;
import org.jfree.data.time.TimePeriodValuesCollection;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.util.ShapeUtilities;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;
public class DisplaySync extends EventDisplay {
// Information to graph for each authority
private TimePeriodValues mDatasetsSync[];
private List<String> mTooltipsSync[];
private CustomXYToolTipGenerator mTooltipGenerators[];
private TimeSeries mDatasetsSyncTickle[];
// Dataset of error events to graph
private TimeSeries mDatasetError;
// State information while processing the event stream
private int mLastState; // 0 if event started, 1 if event stopped
private long mLastStartTime; // ms
private long mLastStopTime; //ms
private String mLastDetails;
private int mLastEvent; // server, poll, etc
public DisplaySync(String name) {
super(name);
}
/**
* Resets the display.
*/
@Override
void resetUI() {
initSyncDisplay();
}
/**
* Creates the UI for the event display.
* @param parent the parent composite.
* @param logParser the current log parser.
* @return the created control (which may have children).
*/
@Override
public Control createComposite(final Composite parent, EventLogParser logParser,
final ILogColumnListener listener) {
Control composite = createCompositeChart(parent, logParser, "Sync Status");
initSyncDisplay();
return composite;
}
/**
* Initialize the Plot and series data for the sync display.
*/
void initSyncDisplay() {
XYPlot xyPlot = mChart.getXYPlot();
XYBarRenderer br = new XYBarRenderer();
mDatasetsSync = new TimePeriodValues[NUM_AUTHS];
mTooltipsSync = new List[NUM_AUTHS];
mTooltipGenerators = new CustomXYToolTipGenerator[NUM_AUTHS];
mLastDetails = "";
TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
xyPlot.setDataset(tpvc);
xyPlot.setRenderer(0, br);
XYLineAndShapeRenderer ls = new XYLineAndShapeRenderer();
ls.setBaseLinesVisible(false);
mDatasetsSyncTickle = new TimeSeries[NUM_AUTHS];
TimeSeriesCollection tsc = new TimeSeriesCollection();
xyPlot.setDataset(1, tsc);
xyPlot.setRenderer(1, ls);
mDatasetError = new TimeSeries("Errors", FixedMillisecond.class);
xyPlot.setDataset(2, new TimeSeriesCollection(mDatasetError));
XYLineAndShapeRenderer errls = new XYLineAndShapeRenderer();
errls.setBaseLinesVisible(false);
errls.setSeriesPaint(0, Color.RED);
xyPlot.setRenderer(2, errls);
for (int i = 0; i < NUM_AUTHS; i++) {
br.setSeriesPaint(i, AUTH_COLORS[i]);
ls.setSeriesPaint(i, AUTH_COLORS[i]);
mDatasetsSync[i] = new TimePeriodValues(AUTH_NAMES[i]);
tpvc.addSeries(mDatasetsSync[i]);
mTooltipsSync[i] = new ArrayList<String>();
mTooltipGenerators[i] = new CustomXYToolTipGenerator();
br.setSeriesToolTipGenerator(i, mTooltipGenerators[i]);
mTooltipGenerators[i].addToolTipSeries(mTooltipsSync[i]);
mDatasetsSyncTickle[i] = new TimeSeries(AUTH_NAMES[i] + " tickle", FixedMillisecond.class);
tsc.addSeries(mDatasetsSyncTickle[i]);
ls.setSeriesShape(i, ShapeUtilities.createUpTriangle(2.5f));
}
}
/**
* Updates the display with a new event. This is the main entry point for
* each event. This method has the logic to tie together the start event,
* stop event, and details event into one graph item. Note that the details
* can happen before or after the stop event.
* @param event The event
* @param logParser the log parser (unused)
*/
@Override
void newEvent(EventContainer event, EventLogParser logParser) {
try {
if (event.mTag == EVENT_SYNC) {
int state = Integer.parseInt(event.getValueAsString(1));
if (state == 0) { // start
mLastStartTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
mLastState = 0;
mLastEvent = Integer.parseInt(event.getValueAsString(2));
mLastDetails = "";
} else if (state == 1) { // stop
if (mLastState == 0) {
mLastStopTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
if (mLastStartTime == 0) {
// Log starts with a stop event
mLastStartTime = mLastStopTime;
}
addEvent(event);
mLastState = 1;
}
}
} else if (event.mTag == EVENT_TICKLE) {
int auth = getAuth(event.getValueAsString(0));
if (auth >= 0) {
long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
mDatasetsSyncTickle[auth].addOrUpdate(new FixedMillisecond(msec), -1);
}
} else if (event.mTag == EVENT_SYNC_DETAILS) {
int auth = getAuth(event.getValueAsString(0));
mLastDetails = event.getValueAsString(3);
if (mLastState != 0) { // Not inside event
long updateTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
if (updateTime - mLastStopTime <= 250) {
// Got details within 250ms after event, so delete and re-insert
// Details later than 250ms (arbitrary) are discarded as probably
// unrelated.
int lastItem = mDatasetsSync[auth].getItemCount();
mDatasetsSync[auth].delete(lastItem-1, lastItem-1);
mTooltipsSync[auth].remove(lastItem-1);
addEvent(event);
}
}
}
} catch (InvalidTypeException e) {
}
}
/**
* Generate the height for an event.
* Height is somewhat arbitrarily the count of "things" that happened
* during the sync.
* When network traffic measurements are available, code should be modified
* to use that instead.
* @param details The details string associated with the event
* @return The height in arbirary units (0-100)
*/
private int getHeightFromDetails(String details) {
if (details == null) {
return 1; // Arbitrary
}
int total = 0;
String parts[] = details.split("[a-zA-Z]");
for (String part : parts) {
if ("".equals(part)) continue;
total += Integer.parseInt(part);
}
if (total == 0) {
total = 1;
}
return total;
}
/**
* Generates the tooltips text for an event.
* This method decodes the cryptic details string.
* @param auth The authority associated with the event
* @param details The details string
* @param eventSource server, poll, etc.
* @return The text to display in the tooltips
*/
private String getTextFromDetails(int auth, String details, int eventSource) {
StringBuffer sb = new StringBuffer();
sb.append(AUTH_NAMES[auth]).append(": \n");
Scanner scanner = new Scanner(details);
Pattern charPat = Pattern.compile("[a-zA-Z]");
Pattern numPat = Pattern.compile("[0-9]+");
while (scanner.hasNext()) {
String key = scanner.findInLine(charPat);
int val = Integer.parseInt(scanner.findInLine(numPat));
if (auth == GMAIL && "M".equals(key)) {
sb.append("messages from server: ").append(val).append("\n");
} else if (auth == GMAIL && "L".equals(key)) {
sb.append("labels from server: ").append(val).append("\n");
} else if (auth == GMAIL && "C".equals(key)) {
sb.append("check conversation requests from server: ").append(val).append("\n");
} else if (auth == GMAIL && "A".equals(key)) {
sb.append("attachments from server: ").append(val).append("\n");
} else if (auth == GMAIL && "U".equals(key)) {
sb.append("op updates from server: ").append(val).append("\n");
} else if (auth == GMAIL && "u".equals(key)) {
sb.append("op updates to server: ").append(val).append("\n");
} else if (auth == GMAIL && "S".equals(key)) {
sb.append("send/receive cycles: ").append(val).append("\n");
} else if ("Q".equals(key)) {
sb.append("queries to server: ").append(val).append("\n");
} else if ("E".equals(key)) {
sb.append("entries from server: ").append(val).append("\n");
} else if ("u".equals(key)) {
sb.append("updates from client: ").append(val).append("\n");
} else if ("i".equals(key)) {
sb.append("inserts from client: ").append(val).append("\n");
} else if ("d".equals(key)) {
sb.append("deletes from client: ").append(val).append("\n");
} else if ("f".equals(key)) {
sb.append("full sync requested\n");
} else if ("r".equals(key)) {
sb.append("partial sync unavailable\n");
} else if ("X".equals(key)) {
sb.append("hard error\n");
} else if ("e".equals(key)) {
sb.append("number of parse exceptions: ").append(val).append("\n");
} else if ("c".equals(key)) {
sb.append("number of conflicts: ").append(val).append("\n");
} else if ("a".equals(key)) {
sb.append("number of auth exceptions: ").append(val).append("\n");
} else if ("D".equals(key)) {
sb.append("too many deletions\n");
} else if ("R".equals(key)) {
sb.append("too many retries: ").append(val).append("\n");
} else if ("b".equals(key)) {
sb.append("database error\n");
} else if ("x".equals(key)) {
sb.append("soft error\n");
} else if ("l".equals(key)) {
sb.append("sync already in progress\n");
} else if ("I".equals(key)) {
sb.append("io exception\n");
} else if (auth == CONTACTS && "p".equals(key)) {
sb.append("photos uploaded from client: ").append(val).append("\n");
} else if (auth == CONTACTS && "P".equals(key)) {
sb.append("photos downloaded from server: ").append(val).append("\n");
} else if (auth == CALENDAR && "F".equals(key)) {
sb.append("server refresh\n");
} else if (auth == CALENDAR && "s".equals(key)) {
sb.append("server diffs fetched\n");
} else {
sb.append(key).append("=").append(val);
}
}
if (eventSource == 0) {
sb.append("(server)");
} else if (eventSource == 1) {
sb.append("(local)");
} else if (eventSource == 2) {
sb.append("(poll)");
} else if (eventSource == 3) {
sb.append("(user)");
}
return sb.toString();
}
/**
* Helper to add an event to the data series.
* Also updates error series if appropriate (x or X in details).
* @param event The event
*/
private void addEvent(EventContainer event) {
try {
int auth = getAuth(event.getValueAsString(0));
double height = getHeightFromDetails(mLastDetails);
height = height / (mLastStopTime - mLastStartTime + 1) * 10000;
if (height > 30) {
height = 30;
}
mDatasetsSync[auth].add(new SimpleTimePeriod(mLastStartTime, mLastStopTime), height);
mTooltipsSync[auth].add(getTextFromDetails(auth, mLastDetails,
mLastEvent));
mTooltipGenerators[auth].addToolTipSeries(mTooltipsSync[auth]);
if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
long msec = (long)event.sec * 1000L + (event.nsec / 1000000L);
mDatasetError.addOrUpdate(new FixedMillisecond(msec), -1);
}
} catch (InvalidTypeException e) {
e.printStackTrace();
}
}
/**
* Gets display type
*
* @return display type as an integer
*/
@Override
int getDisplayType() {
return DISPLAY_TYPE_SYNC;
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright (C) 2008 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.ddmuilib.log.event;
import com.android.ddmlib.log.EventContainer;
import com.android.ddmlib.log.EventLogParser;
import com.android.ddmlib.log.InvalidTypeException;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.RegularTimePeriod;
import org.jfree.data.time.SimpleTimePeriod;
import org.jfree.data.time.TimePeriodValues;
import org.jfree.data.time.TimePeriodValuesCollection;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
public class DisplaySyncHistogram extends EventDisplay {
// State information while processing the event stream
protected int mLastState; // 0 if event started, 1 if event stopped
protected long mLastStartTime; // ms
protected long mLastStopTime; //ms
protected String mLastDetails;
protected int mLastEvent; // server, poll, etc
public DisplaySyncHistogram(String name) {
super(name);
}
/**
* Resets the display.
*/
@Override
void resetUI() {
initSyncHistogramDisplay();
}
/**
* Creates the UI for the event display.
* @param parent the parent composite.
* @param logParser the current log parser.
* @return the created control (which may have children).
*/
@Override
public Control createComposite(final Composite parent, EventLogParser logParser,
final ILogColumnListener listener) {
Control composite = createCompositeChart(parent, logParser, "Sync Histogram");
initSyncHistogramDisplay();
return composite;
}
// Information to graph for each authority
private TimePeriodValues mDatasetsSyncHist[];
/**
* Initializes the display.
*/
private void initSyncHistogramDisplay() {
XYPlot xyPlot = mChart.getXYPlot();
AbstractXYItemRenderer br = new XYBarRenderer();
mDatasetsSyncHist = new TimePeriodValues[NUM_AUTHS+1];
mLastDetails = "";
mTimePeriodMap = new HashMap[NUM_AUTHS + 1];
TimePeriodValuesCollection tpvc = new TimePeriodValuesCollection();
xyPlot.setDataset(tpvc);
xyPlot.setRenderer(br);
for (int i = 0; i < NUM_AUTHS + 1; i++) {
br.setSeriesPaint(i, AUTH_COLORS[i]);
mDatasetsSyncHist[i] = new TimePeriodValues(AUTH_NAMES[i]);
tpvc.addSeries(mDatasetsSyncHist[i]);
mTimePeriodMap[i] = new HashMap<SimpleTimePeriod, Integer>();
}
}
/**
* Updates the display with a new event. This is the main entry point for
* each event. This method has the logic to tie together the start event,
* stop event, and details event into one graph item. Note that the details
* can happen before or after the stop event.
* @param event The event
*/
@Override
void newEvent(EventContainer event, EventLogParser logParser) {
try {
if (event.mTag == EVENT_SYNC) {
int state = Integer.parseInt(event.getValueAsString(1));
if (state == 0) { // start
mLastStartTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
mLastState = 0;
mLastEvent = Integer.parseInt(event.getValueAsString(2));
mLastDetails = "";
} else if (state == 1) { // stop
if (mLastState == 0) {
mLastStopTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
if (mLastStartTime == 0) {
// Log starts with a stop event
mLastStartTime = mLastStopTime;
}
int auth = getAuth(event.getValueAsString(0));
if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
auth = ERRORS;
}
double delta = (mLastStopTime - mLastStartTime) * 100. / 1000 / 3600; // Percent of hour
addHistEvent(event, auth, delta);
mLastState = 1;
}
}
} else if (event.mTag == EVENT_SYNC_DETAILS) {
int auth = getAuth(event.getValueAsString(0));
mLastDetails = event.getValueAsString(3);
if (mLastState != 0) { // Not inside event
long updateTime = (long)event.sec * 1000L + (event.nsec / 1000000L);
if (updateTime - mLastStopTime <= 250) {
// Got details within 250ms after event, so delete and re-insert
// Details later than 250ms (arbitrary) are discarded as probably
// unrelated.
//int lastItem = mDatasetsSync[auth].getItemCount();
//addHistEvent(event);
if (mLastDetails.indexOf('x') >= 0 || mLastDetails.indexOf('X') >= 0) {
// Item turns out to be in error, so transfer time from old auth to error.
double delta = (mLastStopTime - mLastStartTime) * 100. / 1000 / 3600; // Percent of hour
addHistEvent(event, auth, -delta);
addHistEvent(event, ERRORS, delta);
}
}
}
}
} catch (InvalidTypeException e) {
}
}
/**
* Helper to add an event to the data series.
* Also updates error series if appropriate (x or X in details).
* @param event The event
* @param auth
* @param value
*/
private void addHistEvent(EventContainer event, int auth, double value) {
SimpleTimePeriod hour = getTimePeriod(mLastStopTime, mHistWidth);
// Loop over all datasets to do the stacking.
for (int i = auth; i <= ERRORS; i++) {
addToPeriod(mDatasetsSyncHist, i, hour, value);
}
}
Map<SimpleTimePeriod, Integer> mTimePeriodMap[];
private void addToPeriod(TimePeriodValues tpv[], int auth, SimpleTimePeriod period, double value) {
int index;
if (mTimePeriodMap[auth].containsKey(period)) {
index = mTimePeriodMap[auth].get(period);
double oldValue = tpv[auth].getValue(index).doubleValue();
tpv[auth].update(index, oldValue + value);
} else {
index = tpv[auth].getItemCount();
mTimePeriodMap[auth].put(period, index);
tpv[auth].add(period, value);
}
}
/**
* Creates a multiple-hour time period for the histogram.
* @param time Time in milliseconds.
* @param numHoursWide: should divide into a day.
* @return SimpleTimePeriod covering the number of hours and containing time.
*/
private SimpleTimePeriod getTimePeriod(long time, long numHoursWide) {
Date date = new Date(time);
TimeZone zone = RegularTimePeriod.DEFAULT_TIME_ZONE;
Calendar calendar = Calendar.getInstance(zone);
calendar.setTime(date);
long hoursOfYear = calendar.get(Calendar.HOUR_OF_DAY) + calendar.get(Calendar.DAY_OF_YEAR) * 24;
int year = calendar.get(Calendar.YEAR);
hoursOfYear = (hoursOfYear / numHoursWide) * numHoursWide;
calendar.clear();
calendar.set(year, 0, 1, 0, 0); // Jan 1
long start = calendar.getTimeInMillis() + hoursOfYear * 3600 * 1000;
return new SimpleTimePeriod(start, start + numHoursWide * 3600 * 1000);
}
/**
* Gets display type
*
* @return display type as an integer
*/
@Override
int getDisplayType() {
return DISPLAY_TYPE_SYNC_HIST;
}
}

View File

@@ -23,7 +23,6 @@ import com.android.ddmuilib.DdmUiPreferences;
import com.android.ddmuilib.IImageLoader;
import com.android.ddmuilib.log.event.EventDisplay.OccurrenceDisplayDescriptor;
import com.android.ddmuilib.log.event.EventDisplay.ValueDisplayDescriptor;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
@@ -444,10 +443,13 @@ class EventDisplayOptions extends Dialog {
@Override
public void widgetSelected(SelectionEvent e) {
EventDisplay eventDisplay = getCurrentEventDisplay();
if (eventDisplay != null) {
if (eventDisplay != null && eventDisplay.getDisplayType() != mDisplayTypeCombo.getSelectionIndex()) {
/* Replace the EventDisplay object with a different subclass */
setModified();
eventDisplay.setDisplayType(mDisplayTypeCombo.getSelectionIndex());
fillUiWith(eventDisplay);
String name = eventDisplay.getName();
EventDisplay newEventDisplay = EventDisplay.eventDisplayFactory(mDisplayTypeCombo.getSelectionIndex(), name);
setCurrentEventDisplay(newEventDisplay);
fillUiWith(newEventDisplay);
}
}
});
@@ -693,7 +695,7 @@ class EventDisplayOptions extends Dialog {
private void duplicateEventDisplay(ArrayList<EventDisplay> displayList) {
for (EventDisplay eventDisplay : displayList) {
mDisplayList.add(new EventDisplay(eventDisplay));
mDisplayList.add(EventDisplay.clone(eventDisplay));
}
}
@@ -744,7 +746,7 @@ class EventDisplayOptions extends Dialog {
String name = String.format("display %1$d", count + 1);
EventDisplay eventDisplay = new EventDisplay(name);
EventDisplay eventDisplay = EventDisplay.eventDisplayFactory(0 /* type*/, name);
mDisplayList.add(eventDisplay);
mEventDisplayList.add(name);
@@ -780,6 +782,13 @@ class EventDisplayOptions extends Dialog {
return null;
}
private void setCurrentEventDisplay(EventDisplay eventDisplay) {
int selection = mEventDisplayList.getSelectionIndex();
if (selection != -1) {
mDisplayList.set(selection, eventDisplay);
}
}
private void handleEventDisplaySelection() {
EventDisplay eventDisplay = getCurrentEventDisplay();
if (eventDisplay != null) {

View File

@@ -38,7 +38,7 @@ public class EventLogImporter {
if (top == null) {
throw new FileNotFoundException();
}
final String tagFile = top + "/data/etc/event-log-tags";
final String tagFile = top + "/system/core/logcat/event-log-tags";
BufferedReader tagReader = new BufferedReader(
new InputStreamReader(new FileInputStream(tagFile)));
BufferedReader eventReader = new BufferedReader(

View File

@@ -215,6 +215,7 @@ public class ApkBuilder extends BaseBuilder {
// First thing we do is go through the resource delta to not
// lose it if we have to abort the build for any reason.
ApkDeltaVisitor dv = null;
if (kind == FULL_BUILD) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Start_Full_Apk_Build);
@@ -233,22 +234,13 @@ public class ApkBuilder extends BaseBuilder {
mConvertToDex = true;
mBuildFinalPackage = true;
} else {
ApkDeltaVisitor dv = new ApkDeltaVisitor(this, sourceList, outputFolder);
dv = new ApkDeltaVisitor(this, sourceList, outputFolder);
delta.accept(dv);
// save the state
mPackageResources |= dv.getPackageResources();
mConvertToDex |= dv.getConvertToDex();
mBuildFinalPackage |= dv.getMakeFinalPackage();
if (dv.mXmlError) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Xml_Error);
// if there was some XML errors, we just return w/o doing
// anything since we've put some markers in the files anyway
return referencedProjects;
}
}
// also go through the delta for all the referenced projects, until we are forced to
@@ -258,13 +250,13 @@ public class ApkBuilder extends BaseBuilder {
IJavaProject referencedJavaProject = referencedJavaProjects[i];
delta = getDelta(referencedJavaProject.getProject());
if (delta != null) {
ReferencedProjectDeltaVisitor dv = new ReferencedProjectDeltaVisitor(
ReferencedProjectDeltaVisitor refProjectDv = new ReferencedProjectDeltaVisitor(
referencedJavaProject);
delta.accept(dv);
delta.accept(refProjectDv);
// save the state
mConvertToDex |= dv.needDexConvertion();
mBuildFinalPackage |= dv.needMakeFinalPackage();
mConvertToDex |= refProjectDv.needDexConvertion();
mBuildFinalPackage |= refProjectDv.needMakeFinalPackage();
}
}
}
@@ -307,29 +299,14 @@ public class ApkBuilder extends BaseBuilder {
// At this point, we can abort the build if we have to, as we have computed
// our resource delta and stored the result.
abortOnBadSetup(javaProject);
// check if we have finished loading the SDK.
if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
// we exit silently
return referencedProjects;
}
// Now check the compiler compliance level, not displaying the error
// message since this is not the first builder.
if (ProjectHelper.checkCompilerCompliance(getProject())
!= ProjectHelper.COMPILER_COMPLIANCE_OK) {
return referencedProjects;
}
// now check if the project has problem marker already
if (ProjectHelper.hasError(project, true)) {
// we found a marker with error severity: we abort the build.
// Since this is going to happen every time we save a file while
// errors are remaining, we do not force the display of the console, which
// would, in most cases, show on top of the Problem view (which is more
// important in that case).
if (dv != null && dv.mXmlError) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Project_Has_Errors);
Messages.Xml_Error);
// if there was some XML errors, we just return w/o doing
// anything since we've put some markers in the files anyway
return referencedProjects;
}

View File

@@ -19,10 +19,13 @@ package com.android.ide.eclipse.adt.build;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.adt.sdk.LoadStatus;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.common.project.XmlErrorHandler;
import com.android.ide.eclipse.common.project.XmlErrorHandler.XmlErrorListener;
import com.android.sdklib.IAndroidTarget;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
@@ -34,6 +37,8 @@ import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
@@ -841,4 +846,53 @@ abstract class BaseBuilder extends IncrementalProjectBuilder {
return oslibraryList.toArray(new String[oslibraryList.size()]);
}
/**
* Aborts the build if the SDK/project setups are broken. This does not
* display any errors.
*
* @param javaProject The {@link IJavaProject} being compiled.
* @throws CoreException
*/
protected final void abortOnBadSetup(IJavaProject javaProject) throws CoreException {
// check if we have finished loading the SDK.
if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
// we exit silently
stopBuild("SDK is not loaded yet");
}
// check the compiler compliance level.
if (ProjectHelper.checkCompilerCompliance(getProject()) !=
ProjectHelper.COMPILER_COMPLIANCE_OK) {
// we exit silently
stopBuild(Messages.Compiler_Compliance_Error);
}
// Check that the SDK directory has been setup.
String osSdkFolder = AdtPlugin.getOsSdkFolder();
if (osSdkFolder == null || osSdkFolder.length() == 0) {
stopBuild(Messages.No_SDK_Setup_Error);
}
IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(javaProject.getProject());
if (projectTarget == null) {
// no target. error has been output by the container initializer:
// exit silently.
stopBuild("Project has no target");
}
}
/**
* Throws an exception to cancel the build.
*
* @param error the error message
* @param args the printf-style arguments to the error message.
* @throws CoreException
*/
protected final void stopBuild(String error, Object... args) throws CoreException {
throw new CoreException(new Status(IStatus.CANCEL, AdtPlugin.PLUGIN_ID,
String.format(error, args)));
}
}

View File

@@ -20,7 +20,6 @@ import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.project.FixLaunchConfig;
import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.adt.sdk.LoadStatus;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
@@ -275,15 +274,6 @@ public class PreCompilerBuilder extends BaseBuilder {
dv.getAidlToRemove());
}
// if there was some XML errors, we just return w/o doing
// anything since we've put some markers in the files anyway.
if (dv.mXmlError) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Xml_Error);
return null;
}
// get the java package from the visitor
javaPackage = dv.getManifestPackage();
}
@@ -295,38 +285,18 @@ public class PreCompilerBuilder extends BaseBuilder {
// At this point we have stored what needs to be build, so we can
// do some high level test and abort if needed.
abortOnBadSetup(javaProject);
// check if we have finished loading the SDK.
if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
// we exit silently
return null;
}
// check the compiler compliance level, not displaying the error message
// since this is not the first builder.
if (ProjectHelper.checkCompilerCompliance(getProject())
!= ProjectHelper.COMPILER_COMPLIANCE_OK) {
// if there was some XML errors, we just return w/o doing
// anything since we've put some markers in the files anyway.
if (dv != null && dv.mXmlError) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.Compiler_Compliance_Error);
return null;
Messages.Xml_Error);
// This interrupts the build. The next builders will not run.
stopBuild(Messages.Xml_Error);
}
// Check that the SDK directory has been setup.
String osSdkFolder = AdtPlugin.getOsSdkFolder();
if (osSdkFolder == null || osSdkFolder.length() == 0) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.No_SDK_Setup_Error);
markProject(AdtConstants.MARKER_ADT, Messages.No_SDK_Setup_Error,
IMarker.SEVERITY_ERROR);
return null;
}
IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
if (projectTarget == null) {
// no target. error has been output by the container initializer: exit silently.
return null;
}
// get the manifest file
IFile manifest = AndroidManifestHelper.getManifest(project);
@@ -336,7 +306,9 @@ public class PreCompilerBuilder extends BaseBuilder {
AndroidConstants.FN_ANDROID_MANIFEST);
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
return null;
// This interrupts the build. The next builders will not run.
stopBuild(msg);
}
// lets check the XML of the manifest first, if that hasn't been done by the
@@ -353,7 +325,9 @@ public class PreCompilerBuilder extends BaseBuilder {
String msg = String.format(Messages.s_Contains_Xml_Error,
AndroidConstants.FN_ANDROID_MANIFEST);
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, msg);
return null;
// This interrupts the build. The next builders will not run.
stopBuild(msg);
}
// get the java package from the parser
@@ -366,7 +340,9 @@ public class PreCompilerBuilder extends BaseBuilder {
AndroidConstants.FN_ANDROID_MANIFEST);
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
msg);
return null;
// This interrupts the build. The next builders will not run.
stopBuild(msg);
}
// at this point we have the java package. We need to make sure it's not a different package
@@ -409,7 +385,8 @@ public class PreCompilerBuilder extends BaseBuilder {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project, message);
// abort
return null;
// This interrupts the build. The next builders will not run.
stopBuild(message);
}
@@ -430,6 +407,8 @@ public class PreCompilerBuilder extends BaseBuilder {
String osResPath = resLocation.toOSString();
String osManifestPath = manifestLocation.toOSString();
IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
// remove the aapt markers
removeMarkersFromFile(manifest, AndroidConstants.MARKER_AAPT);
removeMarkersFromContainer(resFolder, AndroidConstants.MARKER_AAPT);
@@ -517,20 +496,25 @@ public class PreCompilerBuilder extends BaseBuilder {
Messages.AAPT_Error);
// abort if exec failed.
return null;
// This interrupts the build. The next builders will not run.
stopBuild(Messages.AAPT_Error);
}
} catch (IOException e1) {
// something happen while executing the process,
// mark the project and exit
String msg = String.format(Messages.AAPT_Exec_Error, array.get(0));
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
return null;
// This interrupts the build. The next builders will not run.
stopBuild(msg);
} catch (InterruptedException e) {
// we got interrupted waiting for the process to end...
// mark the project and exit
String msg = String.format(Messages.AAPT_Exec_Error, array.get(0));
markProject(AdtConstants.MARKER_ADT, msg, IMarker.SEVERITY_ERROR);
return null;
// This interrupts the build. The next builders will not run.
stopBuild(msg);
}
// if the return code was OK, we refresh the folder that

View File

@@ -19,16 +19,20 @@ package com.android.ide.eclipse.adt.build;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.project.ProjectHelper;
import com.android.ide.eclipse.adt.sdk.LoadStatus;
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.AndroidConstants;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.sdklib.IAndroidTarget;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import java.util.Map;
@@ -36,7 +40,7 @@ import java.util.Map;
* Resource manager builder whose only purpose is to refresh the resource folder
* so that the other builder use an up to date version.
*/
public class ResourceManagerBuilder extends IncrementalProjectBuilder {
public class ResourceManagerBuilder extends BaseBuilder {
public static final String ID = "com.android.ide.eclipse.adt.ResourceManagerBuilder"; //$NON-NLS-1$
@@ -72,6 +76,38 @@ public class ResourceManagerBuilder extends IncrementalProjectBuilder {
BaseProjectHelper.addMarker(project, AdtConstants.MARKER_ADT, errorMessage,
IMarker.SEVERITY_ERROR);
AdtPlugin.printErrorToConsole(project, errorMessage);
// interrupt the build. The next builders will not run.
stopBuild(errorMessage);
}
// Check that the SDK directory has been setup.
String osSdkFolder = AdtPlugin.getOsSdkFolder();
if (osSdkFolder == null || osSdkFolder.length() == 0) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE, project,
Messages.No_SDK_Setup_Error);
markProject(AdtConstants.MARKER_ADT, Messages.No_SDK_Setup_Error,
IMarker.SEVERITY_ERROR);
// This interrupts the build. The next builders will not run.
stopBuild(Messages.No_SDK_Setup_Error);
}
// check if we have finished loading the SDK.
IJavaProject javaProject = JavaCore.create(project);
if (AdtPlugin.getDefault().getSdkLoadStatus(javaProject) != LoadStatus.LOADED) {
// we exit silently
// This interrupts the build. The next builders will not run.
stopBuild("SDK is not loaded yet");
}
// check the project has a target
IAndroidTarget projectTarget = Sdk.getCurrent().getTarget(project);
if (projectTarget == null) {
// no target. marker has been set by the container initializer: exit silently.
// This interrupts the build. The next builders will not run.
stopBuild("Project has no target");
}
// Check the preference to be sure we are supposed to refresh

View File

@@ -35,8 +35,8 @@ import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.AndroidManifestHelper;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.SdkManager;
import com.android.sdklib.vm.VmManager;
import com.android.sdklib.vm.VmManager.VmInfo;
import com.android.sdklib.avd.AvdManager;
import com.android.sdklib.avd.AvdManager.AvdInfo;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
@@ -74,7 +74,7 @@ import java.util.regex.Pattern;
public final class AndroidLaunchController implements IDebugBridgeChangeListener,
IDeviceChangeListener, IClientChangeListener {
private static final String FLAG_VM = "-vm"; //$NON-NLS-1$
private static final String FLAG_AVD = "-avd"; //$NON-NLS-1$
private static final String FLAG_NETDELAY = "-netdelay"; //$NON-NLS-1$
private static final String FLAG_NETSPEED = "-netspeed"; //$NON-NLS-1$
private static final String FLAG_WIPE_DATA = "-wipe-data"; //$NON-NLS-1$
@@ -228,9 +228,9 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
public boolean mNoBootAnim = LaunchConfigDelegate.DEFAULT_NO_BOOT_ANIM;
/**
* Vm Name.
* AVD Name.
*/
public String mVmName = null;
public String mAvdName = null;
public String mNetworkSpeed = EmulatorConfigTab.getSpeed(
LaunchConfigDelegate.DEFAULT_SPEED);
@@ -262,7 +262,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
}
try {
mVmName = config.getAttribute(LaunchConfigDelegate.ATTR_VM_NAME, mVmName);
mAvdName = config.getAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, mAvdName);
} catch (CoreException e) {
}
@@ -531,8 +531,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
wc.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
LaunchConfigDelegate.DEFAULT_TARGET_MODE);
// default VM: None
wc.setAttribute(LaunchConfigDelegate.ATTR_VM_NAME, (String)null);
// default AVD: None
wc.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, (String)null);
// set the default network speed
wc.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
@@ -629,12 +629,12 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// get the SDK
Sdk currentSdk = Sdk.getCurrent();
VmManager vmManager = currentSdk.getVmManager();
AvdManager avdManager = currentSdk.getAvdManager();
// get the project target
final IAndroidTarget projectTarget = currentSdk.getTarget(project);
// FIXME: check errors on missing sdk, vm manager, or project target.
// FIXME: check errors on missing sdk, AVD manager, or project target.
// device chooser response object.
final DeviceChooserResponse response = new DeviceChooserResponse();
@@ -644,81 +644,81 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
* - Manually Mode
* Always display a UI that lets a user see the current running emulators/devices.
* The UI must show which devices are compatibles, and allow launching new emulators
* with compatible (and not yet running) VM.
* with compatible (and not yet running) AVD.
* - Automatic Way
* * Preferred VM set.
* If Preferred VM is not running: launch it.
* Launch the application on the preferred VM.
* * No preferred VM.
* * Preferred AVD set.
* If Preferred AVD is not running: launch it.
* Launch the application on the preferred AVD.
* * No preferred AVD.
* Count the number of compatible emulators/devices.
* If != 1, display a UI similar to manual mode.
* If == 1, launch the application on this VM/device.
* If == 1, launch the application on this AVD/device.
*/
if (config.mTargetMode == AndroidLaunchConfiguration.AUTO_TARGET_MODE) {
// if we are in automatic target mode, we need to find the current devices
Device[] devices = AndroidDebugBridge.getBridge().getDevices();
// first check if we have a preferred VM name, and if it actually exists, and is valid
// first check if we have a preferred AVD name, and if it actually exists, and is valid
// (ie able to run the project).
// We need to check this in case the VM was recreated with a different target that is
// We need to check this in case the AVD was recreated with a different target that is
// not compatible.
VmInfo preferredVm = null;
if (config.mVmName != null) {
preferredVm = vmManager.getVm(config.mVmName);
if (projectTarget.isCompatibleBaseFor(preferredVm.getTarget()) == false) {
preferredVm = null;
AvdInfo preferredAvd = null;
if (config.mAvdName != null) {
preferredAvd = avdManager.getAvd(config.mAvdName);
if (projectTarget.isCompatibleBaseFor(preferredAvd.getTarget()) == false) {
preferredAvd = null;
AdtPlugin.printErrorToConsole(project, String.format(
"Preferred VM '%1$s' is not compatible with the project target '%2$s'. Looking for a compatible VM...",
config.mVmName, projectTarget.getName()));
"Preferred AVD '%1$s' is not compatible with the project target '%2$s'. Looking for a compatible AVD...",
config.mAvdName, projectTarget.getName()));
}
}
if (preferredVm != null) {
if (preferredAvd != null) {
// look for a matching device
for (Device d : devices) {
String deviceVm = d.getVmName();
if (deviceVm != null && deviceVm.equals(config.mVmName)) {
String deviceAvd = d.getAvdName();
if (deviceAvd != null && deviceAvd.equals(config.mAvdName)) {
response.mustContinue = true;
response.mustLaunchEmulator = false;
response.deviceToUse = d;
AdtPlugin.printToConsole(project, String.format(
"Automatic Target Mode: Preferred VM '%1$s' is available on emulator '%2$s'",
config.mVmName, d));
"Automatic Target Mode: Preferred AVD '%1$s' is available on emulator '%2$s'",
config.mAvdName, d));
continueLaunch(response, project, launch, launchInfo, config);
return;
}
}
// at this point we have a valid preferred VM that is not running.
// at this point we have a valid preferred AVD that is not running.
// We need to start it.
response.mustContinue = true;
response.mustLaunchEmulator = true;
response.vmToLaunch = preferredVm;
response.avdToLaunch = preferredAvd;
AdtPlugin.printToConsole(project, String.format(
"Automatic Target Mode: Preferred VM '%1$s' is not available. Launching new emulator.",
config.mVmName));
"Automatic Target Mode: Preferred AVD '%1$s' is not available. Launching new emulator.",
config.mAvdName));
continueLaunch(response, project, launch, launchInfo, config);
return;
}
// no (valid) preferred VM? look for one.
HashMap<Device, VmInfo> compatibleRunningVms = new HashMap<Device, VmInfo>();
// no (valid) preferred AVD? look for one.
HashMap<Device, AvdInfo> compatibleRunningAvds = new HashMap<Device, AvdInfo>();
boolean hasDevice = false; // if there's 1+ device running, we may force manual mode,
// as we cannot always detect proper compatibility with
// devices. This is the case if the project target is not
// a standard platform
for (Device d : devices) {
String deviceVm = d.getVmName();
if (deviceVm != null) { // physical devices return null.
VmInfo info = vmManager.getVm(deviceVm);
String deviceAvd = d.getAvdName();
if (deviceAvd != null) { // physical devices return null.
AvdInfo info = avdManager.getAvd(deviceAvd);
if (info != null && projectTarget.isCompatibleBaseFor(info.getTarget())) {
compatibleRunningVms.put(d, info);
compatibleRunningAvds.put(d, info);
}
} else {
if (projectTarget.isPlatform()) { // means this can run on any device as long
@@ -728,7 +728,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
int apiNumber = Integer.parseInt(apiString);
if (apiNumber >= projectTarget.getApiVersionNumber()) {
// device is compatible with project
compatibleRunningVms.put(d, null);
compatibleRunningAvds.put(d, null);
continue;
}
} catch (NumberFormatException e) {
@@ -741,54 +741,54 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
// depending on the number of devices, we'll simulate an automatic choice
// from the device chooser or simply show up the device chooser.
if (hasDevice == false && compatibleRunningVms.size() == 0) {
if (hasDevice == false && compatibleRunningAvds.size() == 0) {
// if zero emulators/devices, we launch an emulator.
// We need to figure out which VM first.
// We need to figure out which AVD first.
// we are going to take the closest VM. ie a compatible VM that has the API level
// we are going to take the closest AVD. ie a compatible AVD that has the API level
// closest to the project target.
VmInfo[] vms = vmManager.getVms();
VmInfo defaultVm = null;
for (VmInfo vm : vms) {
if (projectTarget.isCompatibleBaseFor(vm.getTarget())) {
if (defaultVm == null ||
vm.getTarget().getApiVersionNumber() <
defaultVm.getTarget().getApiVersionNumber()) {
defaultVm = vm;
AvdInfo[] avds = avdManager.getAvds();
AvdInfo defaultAvd = null;
for (AvdInfo avd : avds) {
if (projectTarget.isCompatibleBaseFor(avd.getTarget())) {
if (defaultAvd == null ||
avd.getTarget().getApiVersionNumber() <
defaultAvd.getTarget().getApiVersionNumber()) {
defaultAvd = avd;
}
}
}
if (defaultVm != null) {
if (defaultAvd != null) {
response.mustContinue = true;
response.mustLaunchEmulator = true;
response.vmToLaunch = defaultVm;
response.avdToLaunch = defaultAvd;
AdtPlugin.printToConsole(project, String.format(
"Automatic Target Mode: launching new emulator with compatible VM '%1$s'",
defaultVm.getName()));
"Automatic Target Mode: launching new emulator with compatible AVD '%1$s'",
defaultAvd.getName()));
continueLaunch(response, project, launch, launchInfo, config);
return;
} else {
// FIXME: ask the user if he wants to create a VM.
// we found no compatible VM.
// FIXME: ask the user if he wants to create a AVD.
// we found no compatible AVD.
AdtPlugin.printErrorToConsole(project, String.format(
"Failed to find a VM compatible with target '%1$s'. Launch aborted.",
"Failed to find a AVD compatible with target '%1$s'. Launch aborted.",
projectTarget.getName()));
launch.stopLaunch();
return;
}
} else if (hasDevice == false && compatibleRunningVms.size() == 1) {
Entry<Device, VmInfo> e = compatibleRunningVms.entrySet().iterator().next();
} else if (hasDevice == false && compatibleRunningAvds.size() == 1) {
Entry<Device, AvdInfo> e = compatibleRunningAvds.entrySet().iterator().next();
response.mustContinue = true;
response.mustLaunchEmulator = false;
response.deviceToUse = e.getKey();
// get the VmInfo, if null, the device is a physical device.
VmInfo vmInfo = e.getValue();
if (vmInfo != null) {
message = String.format("Automatic Target Mode: using existing emulator '%1$s' running compatible VM '%2$s'",
// get the AvdInfo, if null, the device is a physical device.
AvdInfo avdInfo = e.getValue();
if (avdInfo != null) {
message = String.format("Automatic Target Mode: using existing emulator '%1$s' running compatible AVD '%2$s'",
response.deviceToUse, e.getValue().getName());
} else {
message = String.format("Automatic Target Mode: using device '%1$s'",
@@ -801,7 +801,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
}
// if more than one device, we'll bring up the DeviceChooser dialog below.
if (compatibleRunningVms.size() >= 2) {
if (compatibleRunningAvds.size() >= 2) {
message = "Automatic Target Mode: Several compatible targets. Please select a target device.";
} else if (hasDevice) {
message = "Automatic Target Mode: Unable to detect device compatibility. Please select a target device.";
@@ -849,7 +849,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
synchronized (sListLock) {
mWaitingForEmulatorLaunches.add(launchInfo);
AdtPlugin.printToConsole(project, "Launching a new emulator.");
boolean status = launchEmulator(config, response.vmToLaunch);
boolean status = launchEmulator(config, response.avdToLaunch);
if (status == false) {
// launching the emulator failed!
@@ -1323,7 +1323,7 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
}
}
private boolean launchEmulator(AndroidLaunchConfiguration config, VmInfo vmToLaunch) {
private boolean launchEmulator(AndroidLaunchConfiguration config, AvdInfo avdToLaunch) {
// split the custom command line in segments
ArrayList<String> customArgs = new ArrayList<String>();
@@ -1353,8 +1353,8 @@ public final class AndroidLaunchController implements IDebugBridgeChangeListener
ArrayList<String> list = new ArrayList<String>();
list.add(AdtPlugin.getOsAbsoluteEmulator());
list.add(FLAG_VM);
list.add(vmToLaunch.getName());
list.add(FLAG_AVD);
list.add(avdToLaunch.getName());
if (config.mNetworkSpeed != null) {
list.add(FLAG_NETSPEED);

View File

@@ -31,7 +31,7 @@ import com.android.ide.eclipse.adt.debug.launching.AndroidLaunchController.Delay
import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.ddms.DdmsPlugin;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.vm.VmManager.VmInfo;
import com.android.sdklib.avd.AvdManager.AvdInfo;
import org.eclipse.core.resources.IProject;
import org.eclipse.jface.preference.IPreferenceStore;
@@ -71,7 +71,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
private final static String PREFS_COL_SERIAL = "deviceChooser.serial"; //$NON-NLS-1$
private final static String PREFS_COL_STATE = "deviceChooser.state"; //$NON-NLS-1$
private final static String PREFS_COL_VM = "deviceChooser.vm"; //$NON-NLS-1$
private final static String PREFS_COL_AVD = "deviceChooser.avd"; //$NON-NLS-1$
private final static String PREFS_COL_TARGET = "deviceChooser.target"; //$NON-NLS-1$
private final static String PREFS_COL_DEBUG = "deviceChooser.debug"; //$NON-NLS-1$
@@ -149,8 +149,8 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
return mNoMatchImage;
}
} else {
// get the VmInfo
VmInfo info = mSdk.getVmManager().getVm(device.getVmName());
// get the AvdInfo
AvdInfo info = mSdk.getAvdManager().getAvd(device.getAvdName());
if (info == null) {
return mWarningImage;
}
@@ -171,13 +171,13 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
return device.getSerialNumber();
case 1:
if (device.isEmulator()) {
return device.getVmName();
return device.getAvdName();
} else {
return "N/A"; // devices don't have VM names.
return "N/A"; // devices don't have AVD names.
}
case 2:
if (device.isEmulator()) {
VmInfo info = mSdk.getVmManager().getVm(device.getVmName());
AvdInfo info = mSdk.getAvdManager().getAvd(device.getAvdName());
if (info == null) {
return "?";
}
@@ -221,7 +221,7 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
public static class DeviceChooserResponse {
public boolean mustContinue;
public boolean mustLaunchEmulator;
public VmInfo vmToLaunch;
public AvdInfo avdToLaunch;
public Device deviceToUse;
}
@@ -314,9 +314,9 @@ public class DeviceChooserDialog extends Dialog implements IDeviceChangeListener
SWT.LEFT, "AAA+AAAAAAAAAAAAAAAAAAA", //$NON-NLS-1$
PREFS_COL_SERIAL, store);
TableHelper.createTableColumn(mDeviceTable, "VM Name",
TableHelper.createTableColumn(mDeviceTable, "AVD Name",
SWT.LEFT, "engineering", //$NON-NLS-1$
PREFS_COL_VM, store);
PREFS_COL_AVD, store);
TableHelper.createTableColumn(mDeviceTable, "Target",
SWT.LEFT, "AAA+Android 9.9.9", //$NON-NLS-1$

View File

@@ -80,7 +80,7 @@ public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
*/
public static final String ATTR_ACTIVITY = AdtPlugin.PLUGIN_ID + ".activity"; //$NON-NLS-1$
public static final String ATTR_VM_NAME = AdtPlugin.PLUGIN_ID + ".vm"; //$NON-NLS-1$
public static final String ATTR_AVD_NAME = AdtPlugin.PLUGIN_ID + ".avd"; //$NON-NLS-1$
public static final String ATTR_SPEED = AdtPlugin.PLUGIN_ID + ".speed"; //$NON-NLS-1$

View File

@@ -22,9 +22,9 @@ import com.android.ide.eclipse.adt.sdk.Sdk;
import com.android.ide.eclipse.common.project.BaseProjectHelper;
import com.android.ide.eclipse.ddms.DdmsPlugin;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.vm.VmManager;
import com.android.sdklib.vm.VmManager.VmInfo;
import com.android.sdkuilib.VmSelector;
import com.android.sdklib.avd.AvdManager;
import com.android.sdklib.avd.AvdManager.AvdInfo;
import com.android.sdkuilib.AvdSelector;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
@@ -75,7 +75,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
private Button mAutoTargetButton;
private Button mManualTargetButton;
private VmSelector mPreferredVmSelector;
private AvdSelector mPreferredAvdSelector;
private Combo mSpeedCombo;
@@ -163,11 +163,11 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
}
});
new Label(targetModeGroup, SWT.NONE).setText("Preferred VM");
VmInfo[] vms = new VmInfo[0];
mPreferredVmSelector = new VmSelector(targetModeGroup, vms,
new Label(targetModeGroup, SWT.NONE).setText("Preferred Android Virtual Device");
AvdInfo[] avds = new AvdInfo[0];
mPreferredAvdSelector = new AvdSelector(targetModeGroup, avds,
false /*allowMultipleSelection*/);
mPreferredVmSelector.setSelectionListener(new SelectionAdapter() {
mPreferredAvdSelector.setSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
updateLaunchConfigurationDialog();
@@ -277,7 +277,7 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
* @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration)
*/
public void initializeFrom(ILaunchConfiguration configuration) {
VmManager vmManager = Sdk.getCurrent().getVmManager();
AvdManager avdManager = Sdk.getCurrent().getAvdManager();
boolean value = LaunchConfigDelegate.DEFAULT_TARGET_MODE; // true == automatic
try {
@@ -311,34 +311,34 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
}
}
// update the VM list
VmInfo[] vms = null;
if (vmManager != null) {
vms = vmManager.getVms();
// update the AVD list
AvdInfo[] avds = null;
if (avdManager != null) {
avds = avdManager.getAvds();
}
IAndroidTarget projectTarget = null;
if (project != null) {
projectTarget = Sdk.getCurrent().getTarget(project);
} else {
vms = null; // no project? we don't want to display any "compatible" VMs.
avds = null; // no project? we don't want to display any "compatible" AVDs.
}
mPreferredVmSelector.setVms(vms, projectTarget);
mPreferredAvdSelector.setAvds(avds, projectTarget);
stringValue = "";
try {
stringValue = configuration.getAttribute(LaunchConfigDelegate.ATTR_VM_NAME,
stringValue = configuration.getAttribute(LaunchConfigDelegate.ATTR_AVD_NAME,
stringValue);
} catch (CoreException e) {
// let's not do anything here, we'll use the default value
}
if (stringValue != null && stringValue.length() > 0 && vmManager != null) {
VmInfo targetVm = vmManager.getVm(stringValue);
mPreferredVmSelector.setSelection(targetVm);
if (stringValue != null && stringValue.length() > 0 && avdManager != null) {
AvdInfo targetAvd = avdManager.getAvd(stringValue);
mPreferredAvdSelector.setSelection(targetAvd);
} else {
mPreferredVmSelector.setSelection(null);
mPreferredAvdSelector.setSelection(null);
}
value = LaunchConfigDelegate.DEFAULT_WIPE_DATA;
@@ -404,11 +404,11 @@ public class EmulatorConfigTab extends AbstractLaunchConfigurationTab {
public void performApply(ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(LaunchConfigDelegate.ATTR_TARGET_MODE,
mAutoTargetButton.getSelection());
VmInfo vm = mPreferredVmSelector.getFirstSelected();
if (vm != null) {
configuration.setAttribute(LaunchConfigDelegate.ATTR_VM_NAME, vm.getName());
AvdInfo avd = mPreferredAvdSelector.getFirstSelected();
if (avd != null) {
configuration.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, avd.getName());
} else {
configuration.setAttribute(LaunchConfigDelegate.ATTR_VM_NAME, (String)null);
configuration.setAttribute(LaunchConfigDelegate.ATTR_AVD_NAME, (String)null);
}
configuration.setAttribute(LaunchConfigDelegate.ATTR_SPEED,
mSpeedCombo.getSelectionIndex());

View File

@@ -361,7 +361,7 @@ public final class ProjectHelper {
}
/**
* Returns a {@link IProject} by its running application name, as it returned by the VM.
* Returns a {@link IProject} by its running application name, as it returned by the AVD.
* <p/>
* <var>applicationName</var> will in most case be the package declared in the manifest, but
* can, in some cases, be a custom process name declared in the manifest, in the

View File

@@ -23,9 +23,9 @@ import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
import com.android.sdklib.avd.AvdManager;
import com.android.sdklib.project.ProjectProperties;
import com.android.sdklib.project.ProjectProperties.PropertyType;
import com.android.sdklib.vm.VmManager;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
@@ -52,7 +52,7 @@ public class Sdk {
private static Sdk sCurrentSdk = null;
private final SdkManager mManager;
private final VmManager mVmManager;
private final AvdManager mAvdManager;
private final HashMap<IProject, IAndroidTarget> mProjectMap =
new HashMap<IProject, IAndroidTarget>();
@@ -95,13 +95,13 @@ public class Sdk {
// get an SdkManager object for the location
SdkManager manager = SdkManager.createManager(sdkLocation, log);
if (manager != null) {
VmManager vmManager = null;
AvdManager avdManager = null;
try {
vmManager = new VmManager(manager, log);
avdManager = new AvdManager(manager, log);
} catch (AndroidLocationException e) {
log.error(e, "Error parsing the VMs");
log.error(e, "Error parsing the AVDs");
}
sCurrentSdk = new Sdk(manager, vmManager);
sCurrentSdk = new Sdk(manager, avdManager);
return sCurrentSdk;
} else {
StringBuilder sb = new StringBuilder("Error Loading the SDK:\n");
@@ -255,16 +255,16 @@ public class Sdk {
}
/**
* Returns the {@link VmManager}. If the VmManager failed to parse the VM folder, this could
* Returns the {@link AvdManager}. If the AvdManager failed to parse the AVD folder, this could
* be <code>null</code>.
*/
public VmManager getVmManager() {
return mVmManager;
public AvdManager getAvdManager() {
return mAvdManager;
}
private Sdk(SdkManager manager, VmManager vmManager) {
private Sdk(SdkManager manager, AvdManager avdManager) {
mManager = manager;
mVmManager = vmManager;
mAvdManager = avdManager;
// pre-compute some paths
mDocBaseUrl = getDocumentationBaseUrl(mManager.getLocation() +

View File

@@ -190,7 +190,7 @@ public final class CustomViewDescriptorService {
* @param type
* @param project
* @param typeHierarchy
* @return A ViewElementDescriptor
* @return A ViewElementDescriptor or null if type or typeHierarchy is null.
*/
private ViewElementDescriptor getDescriptor(IType type, IProject project,
ITypeHierarchy typeHierarchy) {
@@ -198,12 +198,17 @@ public final class CustomViewDescriptorService {
List<ElementDescriptor> builtInList = null;
Sdk currentSdk = Sdk.getCurrent();
IAndroidTarget target = currentSdk.getTarget(project);
IAndroidTarget target = currentSdk == null ? null : currentSdk.getTarget(project);
if (target != null) {
AndroidTargetData data = currentSdk.getTargetData(target);
builtInList = data.getLayoutDescriptors().getViewDescriptors();
}
// give up if there's no type
if (type == null) {
return null;
}
String canonicalName = type.getFullyQualifiedName();
if (builtInList != null) {
@@ -218,6 +223,11 @@ public final class CustomViewDescriptorService {
}
// it's not a built-in class? Lets look if the superclass is built-in
// give up if there's no type
if (typeHierarchy == null) {
return null;
}
IType parentType = typeHierarchy.getSuperclass(type);
if (parentType != null) {
ViewElementDescriptor parentDescriptor = getDescriptor(parentType, project,

View File

@@ -79,7 +79,7 @@ public abstract class UiAbstractTextAttributeNode extends UiAttributeNode
public void updateValue(Node xml_attribute_node) {
mCurrentValue = DEFAULT_VALUE;
if (xml_attribute_node != null) {
mCurrentValue = xml_attribute_node.getNodeValue().trim();
mCurrentValue = xml_attribute_node.getNodeValue();
}
if (isValid() && !getTextWidgetValue().equals(mCurrentValue)) {
@@ -101,7 +101,7 @@ public abstract class UiAbstractTextAttributeNode extends UiAttributeNode
public void commit() {
UiElementNode parent = getUiParent();
if (parent != null && isValid() && isDirty()) {
String value = getTextWidgetValue().trim();
String value = getTextWidgetValue();
if (parent.commitAttributeToXml(this, value)) {
mCurrentValue = value;
setDirty(false);

View File

@@ -22,7 +22,6 @@ import com.android.ide.eclipse.mock.JavaProjectMock;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.launching.JavaRuntime;
import junit.framework.TestCase;

View File

@@ -28,7 +28,7 @@ import java.util.HashMap;
import junit.framework.TestCase;
/**
* Unit Test for {@link FrameworkClassLoader}.
* Unit Test for {@link AndroidJarLoader}.
*
* Uses the classes jar.example.Class1/Class2 stored in tests/data/jar_example.jar.
*/
@@ -36,7 +36,7 @@ public class AndroidJarLoaderTest extends TestCase {
private AndroidJarLoader mFrameworkClassLoader;
/** Creates an instance of {@link FrameworkClassLoader} on our test data JAR */
/** Creates an instance of {@link AndroidJarLoader} on our test data JAR */
@Override
public void setUp() throws Exception {
String jarfilePath = AdtTestData.getInstance().getTestFilePath("jar_example.jar"); //$NON-NLS-1$

View File

@@ -35,7 +35,7 @@ import junit.framework.TestCase;
* Test the inner private methods of PlatformDataParser.
*
* Convention: method names that start with an underscore are actually local wrappers
* that call private methods from {@link FrameworkResourceParser} using reflection.
* that call private methods from {@link AndroidTargetParser} using reflection.
* This is inspired by the Python coding rule which mandates underscores prefixes for
* "private" methods.
*/
@@ -131,6 +131,7 @@ public class LayoutParamsParserTest extends TestCase {
//---- access to private methods
/** Calls the private constructor of the parser */
@SuppressWarnings("unused")
private AndroidTargetParser _Constructor(String osJarPath) throws Exception {
Constructor<AndroidTargetParser> constructor =
AndroidTargetParser.class.getDeclaredConstructor(String.class);
@@ -139,6 +140,7 @@ public class LayoutParamsParserTest extends TestCase {
}
/** calls the private getLayoutClasses() of the parser */
@SuppressWarnings("unused")
private void _getLayoutClasses() throws Exception {
Method method = AndroidTargetParser.class.getDeclaredMethod("getLayoutClasses"); //$NON-NLS-1$
method.setAccessible(true);
@@ -146,6 +148,7 @@ public class LayoutParamsParserTest extends TestCase {
}
/** calls the private addGroup() of the parser */
@SuppressWarnings("unused")
private ViewClassInfo _addGroup(Class<?> groupClass) throws Exception {
Method method = LayoutParamsParser.class.getDeclaredMethod("addGroup", //$NON-NLS-1$
IClassDescriptor.class);
@@ -154,6 +157,7 @@ public class LayoutParamsParserTest extends TestCase {
}
/** calls the private addLayoutParams() of the parser */
@SuppressWarnings("unused")
private LayoutParamsInfo _addLayoutParams(Class<?> groupClass) throws Exception {
Method method = LayoutParamsParser.class.getDeclaredMethod("addLayoutParams", //$NON-NLS-1$
IClassDescriptor.class);

View File

@@ -25,9 +25,24 @@ fi
for app in $apps
do
echo '-----------------------------------------------------------'
if [ -d $app/res ]
then
appname=$(basename $app)
resources=
for res in $(echo $app/res/*)
do
resources="$resources $(echo $res | grep -v '\-mcc\|[a-z]*-[a-z][a-z]$\|[a-z]*-[a-z][a-z]-.*')"
done
sources=$app/src
if [ -d $app/tests ]
then
sources="$sources $app/tests"
fi
if [ -d $app/samples ]
then
sources="$sources $app/samples"
fi
# find the R.java file that contains all the generated resource identifiers
rDotJava=$(find out/target/common/obj/APPS/${appname}_intermediates/ -name R.java)
@@ -40,7 +55,7 @@ do
# refer to such constants from java by using an underscore instead of a period, we also
# replace all underscores with a pattern that will match periods and underscores.
p=$(echo $i | sed 's/_/[\\._]/g')
echo $i $(grep -Rw R\\..*\\.$i\\\|@style/$p\\\|@drawable/$p\\\|@anim/$p\\\|@color/$p\\\|@xml/$p\\\|@layout/$p\\\|@menu/$p\\\|@+id/$p\\\|@array/$p\\\|@string/$p $app | wc -l)
echo $i $(grep -Rw R\\..*\\.$i\\\|@style/$p\\\|@drawable/$p\\\|@anim/$p\\\|@color/$p\\\|@xml/$p\\\|@layout/$p\\\|@menu/$p\\\|@+id/$p\\\|@array/$p\\\|@string/$p\\\|@dimen/$p $resources $sources $app/AndroidManifest.xml | wc -l)
done | grep " 0$" | {
# this block gets as its input a list of constants which no references were found, one per line
if [ "$showall" == "yes" ]

View File

@@ -99,8 +99,8 @@ function showUsage() {
#
# In order to define the most common cases simply, "#" can be used for some of
# the fields, with the following default values:
# <test-package> = "#": test class is fully qualified with package
# <build-path> = "#": skip build/sync step
# <test-package> = "#": test class is fully qualified with package
# <test-class> = "#": omit "-e class" section
# <testrunner-package> = "#": use same value as test-package
# <testrunner-component> = "#": use "android.test.InstrumentationTestRunner"
@@ -117,7 +117,8 @@ knownTests=(
"smoke frameworks/base/tests/SmokeTest com.android.smoketest # com.android.smoketest.tests #"
"core frameworks/base/tests/CoreTests # android.core.CoreTests android.core #"
"libcore frameworks/base/tests/CoreTests # android.core.JavaTests android.core #"
"apidemos samples/ApiDemos com.example.android.apis # com.example.android.apis.tests #"
"apidemos development/samples/ApiDemos com.example.android.apis # com.example.android.apis.tests #"
"launchperf development/apps/launchperf com.android.launchperf # # .SimpleActivityLaunchPerformance"
# targeted framework tests
"heap frameworks/base/tests/AndroidTests com.android.unit_tests HeapTest # #"
@@ -130,7 +131,7 @@ knownTests=(
"browser packages/apps/Browser com.android.browser # # .BrowserTestRunner"
"browserfunc packages/apps/Browser com.android.browser # # .BrowserFunctionalTestRunner"
"calendar packages/apps/Calendar/tests com.android.calendar.tests # # #"
"calprov content/providers/calendar com.android.providers.calendar.tests # # #"
"calprov packages/providers/CalendarProvider com.android.providers.calendar # com.android.providers.calendar.tests #"
"camera tests/Camera com.android.cameratests # # CameraInstrumentationTestRunner"
"contactsprov packages/providers/GoogleContactsProvider/tests com.android.providers.contacts # com.android.providers.contactstests #"
"email packages/apps/Email com.android.email # com.android.email.tests #"

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -0,0 +1,16 @@
application: androidappdocs-staging
version: 1
runtime: python
api_version: 1
handlers:
- url: /gae_shell/static
static_dir: gae_shell/static
expiration: 1d
- url: /gae_shell/.*
script: /gae_shell/shell.py
login: admin
- url: .*
script: main.py

View File

@@ -0,0 +1,17 @@
An interactive, stateful AJAX shell that runs Python code on the server.
Part of http://code.google.com/p/google-app-engine-samples/.
May be run as a standalone app or in an existing app as an admin-only handler.
Can be used for system administration tasks, as an interactive way to try out
APIs, or as a debugging aid during development.
The logging, os, sys, db, and users modules are imported automatically.
Interpreter state is stored in the datastore so that variables, function
definitions, and other values in the global and local namespaces can be used
across commands.
To use the shell in your app, copy shell.py, static/*, and templates/* into
your app's source directory. Then, copy the URL handlers from app.yaml into
your app.yaml.

Binary file not shown.

View File

@@ -0,0 +1,308 @@
#!/usr/bin/python
#
# Copyright 2007 Google Inc.
#
# 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.
"""
An interactive, stateful AJAX shell that runs Python code on the server.
Part of http://code.google.com/p/google-app-engine-samples/.
May be run as a standalone app or in an existing app as an admin-only handler.
Can be used for system administration tasks, as an interactive way to try out
APIs, or as a debugging aid during development.
The logging, os, sys, db, and users modules are imported automatically.
Interpreter state is stored in the datastore so that variables, function
definitions, and other values in the global and local namespaces can be used
across commands.
To use the shell in your app, copy shell.py, static/*, and templates/* into
your app's source directory. Then, copy the URL handlers from app.yaml into
your app.yaml.
TODO: unit tests!
"""
import logging
import new
import os
import pickle
import sys
import traceback
import types
import wsgiref.handlers
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
# Set to True if stack traces should be shown in the browser, etc.
_DEBUG = True
# The entity kind for shell sessions. Feel free to rename to suit your app.
_SESSION_KIND = '_Shell_Session'
# Types that can't be pickled.
UNPICKLABLE_TYPES = (
types.ModuleType,
types.TypeType,
types.ClassType,
types.FunctionType,
)
# Unpicklable statements to seed new sessions with.
INITIAL_UNPICKLABLES = [
'import logging',
'import os',
'import sys',
'from google.appengine.ext import db',
'from google.appengine.api import users',
]
class Session(db.Model):
"""A shell session. Stores the session's globals.
Each session globals is stored in one of two places:
If the global is picklable, it's stored in the parallel globals and
global_names list properties. (They're parallel lists to work around the
unfortunate fact that the datastore can't store dictionaries natively.)
If the global is not picklable (e.g. modules, classes, and functions), or if
it was created by the same statement that created an unpicklable global,
it's not stored directly. Instead, the statement is stored in the
unpicklables list property. On each request, before executing the current
statement, the unpicklable statements are evaluated to recreate the
unpicklable globals.
The unpicklable_names property stores all of the names of globals that were
added by unpicklable statements. When we pickle and store the globals after
executing a statement, we skip the ones in unpicklable_names.
Using Text instead of string is an optimization. We don't query on any of
these properties, so they don't need to be indexed.
"""
global_names = db.ListProperty(db.Text)
globals = db.ListProperty(db.Blob)
unpicklable_names = db.ListProperty(db.Text)
unpicklables = db.ListProperty(db.Text)
def set_global(self, name, value):
"""Adds a global, or updates it if it already exists.
Also removes the global from the list of unpicklable names.
Args:
name: the name of the global to remove
value: any picklable value
"""
blob = db.Blob(pickle.dumps(value))
if name in self.global_names:
index = self.global_names.index(name)
self.globals[index] = blob
else:
self.global_names.append(db.Text(name))
self.globals.append(blob)
self.remove_unpicklable_name(name)
def remove_global(self, name):
"""Removes a global, if it exists.
Args:
name: string, the name of the global to remove
"""
if name in self.global_names:
index = self.global_names.index(name)
del self.global_names[index]
del self.globals[index]
def globals_dict(self):
"""Returns a dictionary view of the globals.
"""
return dict((name, pickle.loads(val))
for name, val in zip(self.global_names, self.globals))
def add_unpicklable(self, statement, names):
"""Adds a statement and list of names to the unpicklables.
Also removes the names from the globals.
Args:
statement: string, the statement that created new unpicklable global(s).
names: list of strings; the names of the globals created by the statement.
"""
self.unpicklables.append(db.Text(statement))
for name in names:
self.remove_global(name)
if name not in self.unpicklable_names:
self.unpicklable_names.append(db.Text(name))
def remove_unpicklable_name(self, name):
"""Removes a name from the list of unpicklable names, if it exists.
Args:
name: string, the name of the unpicklable global to remove
"""
if name in self.unpicklable_names:
self.unpicklable_names.remove(name)
class FrontPageHandler(webapp.RequestHandler):
"""Creates a new session and renders the shell.html template.
"""
def get(self):
# set up the session. TODO: garbage collect old shell sessions
session_key = self.request.get('session')
if session_key:
session = Session.get(session_key)
else:
# create a new session
session = Session()
session.unpicklables = [db.Text(line) for line in INITIAL_UNPICKLABLES]
session_key = session.put()
template_file = os.path.join(os.path.dirname(__file__), 'templates',
'shell.html')
session_url = '/?session=%s' % session_key
vars = { 'server_software': os.environ['SERVER_SOFTWARE'],
'python_version': sys.version,
'session': str(session_key),
'user': users.get_current_user(),
'login_url': users.create_login_url(session_url),
'logout_url': users.create_logout_url(session_url),
}
rendered = webapp.template.render(template_file, vars, debug=_DEBUG)
self.response.out.write(rendered)
class StatementHandler(webapp.RequestHandler):
"""Evaluates a python statement in a given session and returns the result.
"""
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
# extract the statement to be run
statement = self.request.get('statement')
if not statement:
return
# the python compiler doesn't like network line endings
statement = statement.replace('\r\n', '\n')
# add a couple newlines at the end of the statement. this makes
# single-line expressions such as 'class Foo: pass' evaluate happily.
statement += '\n\n'
# log and compile the statement up front
try:
logging.info('Compiling and evaluating:\n%s' % statement)
compiled = compile(statement, '<string>', 'single')
except:
self.response.out.write(traceback.format_exc())
return
# create a dedicated module to be used as this statement's __main__
statement_module = new.module('__main__')
# use this request's __builtin__, since it changes on each request.
# this is needed for import statements, among other things.
import __builtin__
statement_module.__builtins__ = __builtin__
# load the session from the datastore
session = Session.get(self.request.get('session'))
# swap in our custom module for __main__. then unpickle the session
# globals, run the statement, and re-pickle the session globals, all
# inside it.
old_main = sys.modules.get('__main__')
try:
sys.modules['__main__'] = statement_module
statement_module.__name__ = '__main__'
# re-evaluate the unpicklables
for code in session.unpicklables:
exec code in statement_module.__dict__
# re-initialize the globals
for name, val in session.globals_dict().items():
try:
statement_module.__dict__[name] = val
except:
msg = 'Dropping %s since it could not be unpickled.\n' % name
self.response.out.write(msg)
logging.warning(msg + traceback.format_exc())
session.remove_global(name)
# run!
old_globals = dict(statement_module.__dict__)
try:
old_stdout = sys.stdout
old_stderr = sys.stderr
try:
sys.stdout = self.response.out
sys.stderr = self.response.out
exec compiled in statement_module.__dict__
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
except:
self.response.out.write(traceback.format_exc())
return
# extract the new globals that this statement added
new_globals = {}
for name, val in statement_module.__dict__.items():
if name not in old_globals or val != old_globals[name]:
new_globals[name] = val
if True in [isinstance(val, UNPICKLABLE_TYPES)
for val in new_globals.values()]:
# this statement added an unpicklable global. store the statement and
# the names of all of the globals it added in the unpicklables.
session.add_unpicklable(statement, new_globals.keys())
logging.debug('Storing this statement as an unpicklable.')
else:
# this statement didn't add any unpicklables. pickle and store the
# new globals back into the datastore.
for name, val in new_globals.items():
if not name.startswith('__'):
session.set_global(name, val)
finally:
sys.modules['__main__'] = old_main
session.put()
def main():
application = webapp.WSGIApplication(
[('/gae_shell/', FrontPageHandler),
('/gae_shell/shell.do', StatementHandler)], debug=_DEBUG)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,308 @@
#!/usr/bin/python
#
# Copyright 2007 Google Inc.
#
# 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.
"""
An interactive, stateful AJAX shell that runs Python code on the server.
Part of http://code.google.com/p/google-app-engine-samples/.
May be run as a standalone app or in an existing app as an admin-only handler.
Can be used for system administration tasks, as an interactive way to try out
APIs, or as a debugging aid during development.
The logging, os, sys, db, and users modules are imported automatically.
Interpreter state is stored in the datastore so that variables, function
definitions, and other values in the global and local namespaces can be used
across commands.
To use the shell in your app, copy shell.py, static/*, and templates/* into
your app's source directory. Then, copy the URL handlers from app.yaml into
your app.yaml.
TODO: unit tests!
"""
import logging
import new
import os
import pickle
import sys
import traceback
import types
import wsgiref.handlers
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
# Set to True if stack traces should be shown in the browser, etc.
_DEBUG = True
# The entity kind for shell sessions. Feel free to rename to suit your app.
_SESSION_KIND = '_Shell_Session'
# Types that can't be pickled.
UNPICKLABLE_TYPES = (
types.ModuleType,
types.TypeType,
types.ClassType,
types.FunctionType,
)
# Unpicklable statements to seed new sessions with.
INITIAL_UNPICKLABLES = [
'import logging',
'import os',
'import sys',
'from google.appengine.ext import db',
'from google.appengine.api import users',
]
class Session(db.Model):
"""A shell session. Stores the session's globals.
Each session globals is stored in one of two places:
If the global is picklable, it's stored in the parallel globals and
global_names list properties. (They're parallel lists to work around the
unfortunate fact that the datastore can't store dictionaries natively.)
If the global is not picklable (e.g. modules, classes, and functions), or if
it was created by the same statement that created an unpicklable global,
it's not stored directly. Instead, the statement is stored in the
unpicklables list property. On each request, before executing the current
statement, the unpicklable statements are evaluated to recreate the
unpicklable globals.
The unpicklable_names property stores all of the names of globals that were
added by unpicklable statements. When we pickle and store the globals after
executing a statement, we skip the ones in unpicklable_names.
Using Text instead of string is an optimization. We don't query on any of
these properties, so they don't need to be indexed.
"""
global_names = db.ListProperty(db.Text)
globals = db.ListProperty(db.Blob)
unpicklable_names = db.ListProperty(db.Text)
unpicklables = db.ListProperty(db.Text)
def set_global(self, name, value):
"""Adds a global, or updates it if it already exists.
Also removes the global from the list of unpicklable names.
Args:
name: the name of the global to remove
value: any picklable value
"""
blob = db.Blob(pickle.dumps(value))
if name in self.global_names:
index = self.global_names.index(name)
self.globals[index] = blob
else:
self.global_names.append(db.Text(name))
self.globals.append(blob)
self.remove_unpicklable_name(name)
def remove_global(self, name):
"""Removes a global, if it exists.
Args:
name: string, the name of the global to remove
"""
if name in self.global_names:
index = self.global_names.index(name)
del self.global_names[index]
del self.globals[index]
def globals_dict(self):
"""Returns a dictionary view of the globals.
"""
return dict((name, pickle.loads(val))
for name, val in zip(self.global_names, self.globals))
def add_unpicklable(self, statement, names):
"""Adds a statement and list of names to the unpicklables.
Also removes the names from the globals.
Args:
statement: string, the statement that created new unpicklable global(s).
names: list of strings; the names of the globals created by the statement.
"""
self.unpicklables.append(db.Text(statement))
for name in names:
self.remove_global(name)
if name not in self.unpicklable_names:
self.unpicklable_names.append(db.Text(name))
def remove_unpicklable_name(self, name):
"""Removes a name from the list of unpicklable names, if it exists.
Args:
name: string, the name of the unpicklable global to remove
"""
if name in self.unpicklable_names:
self.unpicklable_names.remove(name)
class FrontPageHandler(webapp.RequestHandler):
"""Creates a new session and renders the shell.html template.
"""
def get(self):
# set up the session. TODO: garbage collect old shell sessions
session_key = self.request.get('session')
if session_key:
session = Session.get(session_key)
else:
# create a new session
session = Session()
session.unpicklables = [db.Text(line) for line in INITIAL_UNPICKLABLES]
session_key = session.put()
template_file = os.path.join(os.path.dirname(__file__), 'templates',
'shell.html')
session_url = '/?session=%s' % session_key
vars = { 'server_software': os.environ['SERVER_SOFTWARE'],
'python_version': sys.version,
'session': str(session_key),
'user': users.get_current_user(),
'login_url': users.create_login_url(session_url),
'logout_url': users.create_logout_url(session_url),
}
rendered = webapp.template.render(template_file, vars, debug=_DEBUG)
self.response.out.write(rendered)
class StatementHandler(webapp.RequestHandler):
"""Evaluates a python statement in a given session and returns the result.
"""
def get(self):
self.response.headers['Content-Type'] = 'text/plain'
# extract the statement to be run
statement = self.request.get('statement')
if not statement:
return
# the python compiler doesn't like network line endings
statement = statement.replace('\r\n', '\n')
# add a couple newlines at the end of the statement. this makes
# single-line expressions such as 'class Foo: pass' evaluate happily.
statement += '\n\n'
# log and compile the statement up front
try:
logging.info('Compiling and evaluating:\n%s' % statement)
compiled = compile(statement, '<string>', 'single')
except:
self.response.out.write(traceback.format_exc())
return
# create a dedicated module to be used as this statement's __main__
statement_module = new.module('__main__')
# use this request's __builtin__, since it changes on each request.
# this is needed for import statements, among other things.
import __builtin__
statement_module.__builtins__ = __builtin__
# load the session from the datastore
session = Session.get(self.request.get('session'))
# swap in our custom module for __main__. then unpickle the session
# globals, run the statement, and re-pickle the session globals, all
# inside it.
old_main = sys.modules.get('__main__')
try:
sys.modules['__main__'] = statement_module
statement_module.__name__ = '__main__'
# re-evaluate the unpicklables
for code in session.unpicklables:
exec code in statement_module.__dict__
# re-initialize the globals
for name, val in session.globals_dict().items():
try:
statement_module.__dict__[name] = val
except:
msg = 'Dropping %s since it could not be unpickled.\n' % name
self.response.out.write(msg)
logging.warning(msg + traceback.format_exc())
session.remove_global(name)
# run!
old_globals = dict(statement_module.__dict__)
try:
old_stdout = sys.stdout
old_stderr = sys.stderr
try:
sys.stdout = self.response.out
sys.stderr = self.response.out
exec compiled in statement_module.__dict__
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
except:
self.response.out.write(traceback.format_exc())
return
# extract the new globals that this statement added
new_globals = {}
for name, val in statement_module.__dict__.items():
if name not in old_globals or val != old_globals[name]:
new_globals[name] = val
if True in [isinstance(val, UNPICKLABLE_TYPES)
for val in new_globals.values()]:
# this statement added an unpicklable global. store the statement and
# the names of all of the globals it added in the unpicklables.
session.add_unpicklable(statement, new_globals.keys())
logging.debug('Storing this statement as an unpicklable.')
else:
# this statement didn't add any unpicklables. pickle and store the
# new globals back into the datastore.
for name, val in new_globals.items():
if not name.startswith('__'):
session.set_global(name, val)
finally:
sys.modules['__main__'] = old_main
session.put()
def main():
application = webapp.WSGIApplication(
[('/', FrontPageHandler),
('/shell.do', StatementHandler)], debug=_DEBUG)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,195 @@
// Copyright 2007 Google Inc.
//
// 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.
/**
* @fileoverview
* Javascript code for the interactive AJAX shell.
*
* Part of http://code.google.com/p/google-app-engine-samples/.
*
* Includes a function (shell.runStatement) that sends the current python
* statement in the shell prompt text box to the server, and a callback
* (shell.done) that displays the results when the XmlHttpRequest returns.
*
* Also includes cross-browser code (shell.getXmlHttpRequest) to get an
* XmlHttpRequest.
*/
/**
* Shell namespace.
* @type {Object}
*/
var shell = {}
/**
* The shell history. history is an array of strings, ordered oldest to
* newest. historyCursor is the current history element that the user is on.
*
* The last history element is the statement that the user is currently
* typing. When a statement is run, it's frozen in the history, a new history
* element is added to the end of the array for the new statement, and
* historyCursor is updated to point to the new element.
*
* @type {Array}
*/
shell.history = [''];
/**
* See {shell.history}
* @type {number}
*/
shell.historyCursor = 0;
/**
* A constant for the XmlHttpRequest 'done' state.
* @type Number
*/
shell.DONE_STATE = 4;
/**
* A cross-browser function to get an XmlHttpRequest object.
*
* @return {XmlHttpRequest?} a new XmlHttpRequest
*/
shell.getXmlHttpRequest = function() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
return new ActiveXObject('Msxml2.XMLHTTP');
} catch(e) {
return new ActiveXObject('Microsoft.XMLHTTP');
}
}
return null;
};
/**
* This is the prompt textarea's onkeypress handler. Depending on the key that
* was pressed, it will run the statement, navigate the history, or update the
* current statement in the history.
*
* @param {Event} event the keypress event
* @return {Boolean} false to tell the browser not to submit the form.
*/
shell.onPromptKeyPress = function(event) {
var statement = document.getElementById('statement');
if (this.historyCursor == this.history.length - 1) {
// we're on the current statement. update it in the history before doing
// anything.
this.history[this.historyCursor] = statement.value;
}
// should we pull something from the history?
if (event.ctrlKey && event.keyCode == 38 /* up arrow */) {
if (this.historyCursor > 0) {
statement.value = this.history[--this.historyCursor];
}
return false;
} else if (event.ctrlKey && event.keyCode == 40 /* down arrow */) {
if (this.historyCursor < this.history.length - 1) {
statement.value = this.history[++this.historyCursor];
}
return false;
} else if (!event.altKey) {
// probably changing the statement. update it in the history.
this.historyCursor = this.history.length - 1;
this.history[this.historyCursor] = statement.value;
}
// should we submit?
var ctrlEnter = (document.getElementById('submit_key').value == 'ctrl-enter');
if (event.keyCode == 13 /* enter */ && !event.altKey && !event.shiftKey &&
event.ctrlKey == ctrlEnter) {
return this.runStatement();
}
};
/**
* The XmlHttpRequest callback. If the request succeeds, it adds the command
* and its resulting output to the shell history div.
*
* @param {XmlHttpRequest} req the XmlHttpRequest we used to send the current
* statement to the server
*/
shell.done = function(req) {
if (req.readyState == this.DONE_STATE) {
var statement = document.getElementById('statement')
statement.className = 'prompt';
// add the command to the shell output
var output = document.getElementById('output');
output.value += '\n>>> ' + statement.value;
statement.value = '';
// add a new history element
this.history.push('');
this.historyCursor = this.history.length - 1;
// add the command's result
var result = req.responseText.replace(/^\s*|\s*$/g, ''); // trim whitespace
if (result != '')
output.value += '\n' + result;
// scroll to the bottom
output.scrollTop = output.scrollHeight;
if (output.createTextRange) {
var range = output.createTextRange();
range.collapse(false);
range.select();
}
}
};
/**
* This is the form's onsubmit handler. It sends the python statement to the
* server, and registers shell.done() as the callback to run when it returns.
*
* @return {Boolean} false to tell the browser not to submit the form.
*/
shell.runStatement = function() {
var form = document.getElementById('form');
// build a XmlHttpRequest
var req = this.getXmlHttpRequest();
if (!req) {
document.getElementById('ajax-status').innerHTML =
"<span class='error'>Your browser doesn't support AJAX. :(</span>";
return false;
}
req.onreadystatechange = function() { shell.done(req); };
// build the query parameter string
var params = '';
for (i = 0; i < form.elements.length; i++) {
var elem = form.elements[i];
if (elem.type != 'submit' && elem.type != 'button' && elem.id != 'caret') {
var value = escape(elem.value).replace(/\+/g, '%2B'); // escape ignores +
params += '&' + elem.name + '=' + value;
}
}
// send the request and tell the user.
document.getElementById('statement').className = 'prompt processing';
req.open(form.method, form.action + '?' + params, true);
req.setRequestHeader('Content-type',
'application/x-www-form-urlencoded;charset=UTF-8');
req.send(null);
return false;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,122 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title> Interactive Shell </title>
<script type="text/javascript" src="/gae_shell/static/shell.js"></script>
<style type="text/css">
body {
font-family: monospace;
font-size: 10pt;
}
p {
margin: 0.5em;
}
.prompt, #output {
width: 45em;
border: 1px solid silver;
background-color: #f5f5f5;
font-size: 10pt;
margin: 0.5em;
padding: 0.5em;
padding-right: 0em;
overflow-x: hidden;
}
#toolbar {
margin-left: 0.5em;
padding-left: 0.5em;
}
#caret {
width: 2.5em;
margin-right: 0px;
padding-right: 0px;
border-right: 0px;
}
#statement {
width: 43em;
margin-left: -1em;
padding-left: 0px;
border-left: 0px;
background-position: top right;
background-repeat: no-repeat;
}
.processing {
background-image: url("/gae_shell/static/spinner.gif");
}
#ajax-status {
font-weight: bold;
}
.message {
color: #8AD;
font-weight: bold;
font-style: italic;
}
.error {
color: #F44;
}
.username {
font-weight: bold;
}
</style>
</head>
<body>
<p> Interactive server-side Python shell for
<a href="http://code.google.com/appengine/">Google App Engine</a>.
(<a href="http://code.google.com/p/google-app-engine-samples/">source</a>)
</p>
<textarea id="output" rows="22" readonly="readonly">
{{ server_software }}
Python {{ python_version }}
</textarea>
<form id="form" action="shell.do" method="get">
<nobr>
<textarea class="prompt" id="caret" readonly="readonly" rows="4"
onfocus="document.getElementById('statement').focus()"
>&gt;&gt;&gt;</textarea>
<textarea class="prompt" name="statement" id="statement" rows="4"
onkeypress="return shell.onPromptKeyPress(event);"></textarea>
</nobr>
<input type="hidden" name="session" value="{{ session }}" />
<input type="submit" style="display: none" />
</form>
<p id="ajax-status"></p>
<p id="toolbar">
{% if user %}
<span class="username">{{ user.nickname }}</span>
(<a href="{{ logout_url }}">log out</a>)
{% else %}
<a href="{{ login_url }}">log in</a>
{% endif %}
| Ctrl-Up/Down for history |
<select id="submit_key">
<option value="enter">Enter</option>
<option value="ctrl-enter" selected="selected">Ctrl-Enter</option>
</select>
<label for="submit_key">submits</label>
</p>
<script type="text/javascript">
document.getElementById('statement').focus();
</script>
</body>
</html>

View File

@@ -0,0 +1,12 @@
indexes:
# AUTOGENERATED
# This index.yaml is automatically updated whenever the dev_appserver
# detects that a new type of query is run. If you want to manage the
# index.yaml file manually, remove the above marker line (the line
# saying "# AUTOGENERATED"). If you want to manage some indexes
# manually, move them above the marker line. The index.yaml file is
# automatically uploaded to the admin console when you next deploy
# your application using appcfg.py.

View File

@@ -0,0 +1,412 @@
#!/usr/bin/env python
#
# Copyright 2009 Google Inc.
#
# 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.
#
"""A class to serve pages from zip files and use memcache for performance.
This contains a class and a function to create an anonymous instance of the
class to serve HTTP GET requests. Memcache is used to increase response speed
and lower processing cycles used in serving. Credit to Guido van Rossum and
his implementation of zipserve which served as a reference as I wrote this.
MemcachedZipHandler: Class that serves request
create_handler: method to create instance of MemcachedZipHandler
"""
__author__ = 'jmatt@google.com (Justin Mattson)'
import email.Utils
import logging
import mimetypes
import time
import zipfile
from google.appengine.api import memcache
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
def create_handler(zip_files, max_age=None, public=None):
"""Factory method to create a MemcachedZipHandler instance.
Args:
zip_files: A list of file names, or a list of lists of file name, first
member of file mappings. See MemcachedZipHandler documentation for
more information about using the list of lists format
max_age: The maximum client-side cache lifetime
public: Whether this should be declared public in the client-side cache
Returns:
A MemcachedZipHandler wrapped in a pretty, anonymous bow for use with App
Engine
Raises:
ValueError: if the zip_files argument is not a list
"""
# verify argument integrity. If the argument is passed in list format,
# convert it to list of lists format
if zip_files and type(zip_files).__name__ == 'list':
num_items = len(zip_files)
while num_items > 0:
if type(zip_files[num_items - 1]).__name__ != 'list':
zip_files[num_items - 1] = [zip_files[num_items-1]]
num_items -= 1
else:
raise ValueError('File name arguments must be a list')
class HandlerWrapper(MemcachedZipHandler):
"""Simple wrapper for an instance of MemcachedZipHandler.
I'm still not sure why this is needed
"""
def get(self, name):
self.zipfilenames = zip_files
self.TrueGet(name)
if max_age is not None:
MAX_AGE = max_age
if public is not None:
PUBLIC = public
return HandlerWrapper
class MemcachedZipHandler(webapp.RequestHandler):
"""Handles get requests for a given URL.
Serves a GET request from a series of zip files. As files are served they are
put into memcache, which is much faster than retreiving them from the zip
source file again. It also uses considerably fewer CPU cycles.
"""
zipfile_cache = {} # class cache of source zip files
MAX_AGE = 600 # max client-side cache lifetime
PUBLIC = True # public cache setting
CACHE_PREFIX = 'cache://' # memcache key prefix for actual URLs
NEG_CACHE_PREFIX = 'noncache://' # memcache key prefix for non-existant URL
def TrueGet(self, name):
"""The top-level entry point to serving requests.
Called 'True' get because it does the work when called from the wrapper
class' get method
Args:
name: URL requested
Returns:
None
"""
name = self.PreprocessUrl(name)
# see if we have the page in the memcache
resp_data = self.GetFromCache(name)
if resp_data is None:
logging.info('Cache miss for %s', name)
resp_data = self.GetFromNegativeCache(name)
if resp_data is None:
resp_data = self.GetFromStore(name)
# IF we have the file, put it in the memcache
# ELSE put it in the negative cache
if resp_data is not None:
self.StoreOrUpdateInCache(name, resp_data)
else:
logging.info('Adding %s to negative cache, serving 404', name)
self.StoreInNegativeCache(name)
self.Write404Error()
return
else:
self.Write404Error()
return
content_type, encoding = mimetypes.guess_type(name)
if content_type:
self.response.headers['Content-Type'] = content_type
self.SetCachingHeaders()
self.response.out.write(resp_data)
def PreprocessUrl(self, name):
"""Any preprocessing work on the URL when it comes it.
Put any work related to interpretting the incoming URL here. For example,
this is used to redirect requests for a directory to the index.html file
in that directory. Subclasses should override this method to do different
preprocessing.
Args:
name: The incoming URL
Returns:
The processed URL
"""
# handle special case of requesting the domain itself
if not name:
name = 'index.html'
# determine if this is a request for a directory
final_path_segment = name
final_slash_offset = name.rfind('/')
if final_slash_offset != len(name) - 1:
final_path_segment = name[final_slash_offset + 1:]
if final_path_segment.find('.') == -1:
name = ''.join([name, '/'])
# if this is a directory, redirect to index.html
if name[len(name) - 1:] == '/':
return '%s%s' % (name, 'index.html')
else:
return name
def GetFromStore(self, file_path):
"""Retrieve file from zip files.
Get the file from the source, it must not have been in the memcache. If
possible, we'll use the zip file index to quickly locate where the file
should be found. (See MapToFileArchive documentation for assumptions about
file ordering.) If we don't have an index or don't find the file where the
index says we should, look through all the zip files to find it.
Args:
file_path: the file that we're looking for
Returns:
The contents of the requested file
"""
resp_data = None
file_itr = iter(self.zipfilenames)
# check the index, if we have one, to see what archive the file is in
archive_name = self.MapFileToArchive(file_path)
if not archive_name:
archive_name = file_itr.next()[0]
while resp_data is None and archive_name:
zip_archive = self.LoadZipFile(archive_name)
if zip_archive:
# we expect some lookups will fail, and that's okay, 404s will deal
# with that
try:
resp_data = zip_archive.read(file_path)
except (KeyError, RuntimeError), err:
# no op
x = False
if resp_data is not None:
logging.info('%s read from %s', file_path, archive_name)
try:
archive_name = file_itr.next()[0]
except (StopIteration), err:
archive_name = False
return resp_data
def LoadZipFile(self, zipfilename):
"""Convenience method to load zip file.
Just a convenience method to load the zip file from the data store. This is
useful if we ever want to change data stores and also as a means of
dependency injection for testing. This method will look at our file cache
first, and then load and cache the file if there's a cache miss
Args:
zipfilename: the name of the zip file to load
Returns:
The zip file requested, or None if there is an I/O error
"""
zip_archive = None
zip_archive = self.zipfile_cache.get(zipfilename)
if zip_archive is None:
try:
zip_archive = zipfile.ZipFile(zipfilename)
self.zipfile_cache[zipfilename] = zip_archive
except (IOError, RuntimeError), err:
logging.error('Can\'t open zipfile %s, cause: %s' % (zipfilename,
err))
return zip_archive
def MapFileToArchive(self, file_path):
"""Given a file name, determine what archive it should be in.
This method makes two critical assumptions.
(1) The zip files passed as an argument to the handler, if concatenated
in that same order, would result in a total ordering
of all the files. See (2) for ordering type.
(2) Upper case letters before lower case letters. The traversal of a
directory tree is depth first. A parent directory's files are added
before the files of any child directories
Args:
file_path: the file to be mapped to an archive
Returns:
The name of the archive where we expect the file to be
"""
num_archives = len(self.zipfilenames)
while num_archives > 0:
target = self.zipfilenames[num_archives - 1]
if len(target) > 1:
if self.CompareFilenames(target[1], file_path) >= 0:
return target[0]
num_archives -= 1
return None
def CompareFilenames(self, file1, file2):
"""Determines whether file1 is lexigraphically 'before' file2.
WARNING: This method assumes that paths are output in a depth-first,
with parent directories' files stored before childs'
We say that file1 is lexigraphically before file2 if the last non-matching
path segment of file1 is alphabetically before file2.
Args:
file1: the first file path
file2: the second file path
Returns:
A positive number if file1 is before file2
A negative number if file2 is before file1
0 if filenames are the same
"""
f1_segments = file1.split('/')
f2_segments = file2.split('/')
segment_ptr = 0
while (segment_ptr < len(f1_segments) and
segment_ptr < len(f2_segments) and
f1_segments[segment_ptr] == f2_segments[segment_ptr]):
segment_ptr += 1
if len(f1_segments) == len(f2_segments):
# we fell off the end, the paths much be the same
if segment_ptr == len(f1_segments):
return 0
# we didn't fall of the end, compare the segments where they differ
if f1_segments[segment_ptr] < f2_segments[segment_ptr]:
return 1
elif f1_segments[segment_ptr] > f2_segments[segment_ptr]:
return -1
else:
return 0
# the number of segments differs, we either mismatched comparing
# directories, or comparing a file to a directory
else:
# IF we were looking at the last segment of one of the paths,
# the one with fewer segments is first because files come before
# directories
# ELSE we just need to compare directory names
if (segment_ptr + 1 == len(f1_segments) or
segment_ptr + 1 == len(f2_segments)):
return len(f2_segments) - len(f1_segments)
else:
if f1_segments[segment_ptr] < f2_segments[segment_ptr]:
return 1
elif f1_segments[segment_ptr] > f2_segments[segment_ptr]:
return -1
else:
return 0
def SetCachingHeaders(self):
"""Set caching headers for the request."""
max_age = self.MAX_AGE
self.response.headers['Expires'] = email.Utils.formatdate(
time.time() + max_age, usegmt=True)
cache_control = []
if self.PUBLIC:
cache_control.append('public')
cache_control.append('max-age=%d' % max_age)
self.response.headers['Cache-Control'] = ', '.join(cache_control)
def GetFromCache(self, filename):
"""Get file from memcache, if available.
Args:
filename: The URL of the file to return
Returns:
The content of the file
"""
return memcache.get('%s%s' % (self.CACHE_PREFIX, filename))
def StoreOrUpdateInCache(self, filename, data):
"""Store data in the cache.
Store a piece of data in the memcache. Memcache has a maximum item size of
1*10^6 bytes. If the data is too large, fail, but log the failure. Future
work will consider compressing the data before storing or chunking it
Args:
filename: the name of the file to store
data: the data of the file
Returns:
None
"""
try:
if not memcache.add('%s%s' % (self.CACHE_PREFIX, filename), data):
memcache.replace('%s%s' % (self.CACHE_PREFIX, filename), data)
except (ValueError), err:
logging.warning('Data size too large to cache\n%s' % err)
def Write404Error(self):
"""Ouptut a simple 404 response."""
self.error(404)
self.response.out.write(
''.join(['<html><head><title>404: Not Found</title></head>',
'<body><b><h2>Error 404</h2><br/>',
'File not found</b></body></html>']))
def StoreInNegativeCache(self, filename):
"""If a non-existant URL is accessed, cache this result as well.
Future work should consider setting a maximum negative cache size to
prevent it from from negatively impacting the real cache.
Args:
filename: URL to add ot negative cache
Returns:
None
"""
memcache.add('%s%s' % (self.NEG_CACHE_PREFIX, filename), -1)
def GetFromNegativeCache(self, filename):
"""Retrieve from negative cache.
Args:
filename: URL to retreive
Returns:
The file contents if present in the negative cache.
"""
return memcache.get('%s%s' % (self.NEG_CACHE_PREFIX, filename))
def main():
application = webapp.WSGIApplication([('/([^/]+)/(.*)',
MemcachedZipHandler)])
util.run_wsgi_app(application)
if __name__ == '__main__':
main()

View File

@@ -3,21 +3,35 @@
function replace()
{
echo replacing $1
rm -rf $UNZIPPED_BASE_DIR/$1
cp -rf $UNZIPPED_IMAGE_DIR/$1 $UNZIPPED_BASE_DIR/$1
rm $V -rf "$UNZIPPED_BASE_DIR"/$1
cp $V -rf "$UNZIPPED_IMAGE_DIR"/$1 "$UNZIPPED_BASE_DIR"/$1
}
BASE=$1
IMAGES=$2
OUTPUT=$3
V=""
Q="-q"
if [ "$1" == "-v" ]; then
V="-v"
Q=""
shift
fi
if [[ -z $BASE || -z $IMAGES || -z $OUTPUT ]] ; then
echo "usage: combine_sdks.sh BASE IMAGES OUTPUT"
NOZIP=""
if [ "$1" == "-nozip" ]; then
NOZIP="1"
shift
fi
BASE="$1"
IMAGES="$2"
OUTPUT="$3"
if [[ -z "$BASE" || -z "$IMAGES" || -z "$OUTPUT" ]] ; then
echo "usage: combine_sdks.sh [-v] [-nozip] BASE IMAGES OUTPUT"
echo
echo " BASE and IMAGES should be sdk zip files. The system image files,"
echo " emulator and other runtime files will be copied from IMAGES and"
echo " everything else will be copied from BASE. All of this will be"
echo " bundled into OUTPUT and zipped up again."
echo " bundled into OUTPUT and zipped up again (unless -nozip is specified)."
echo
exit 1
fi
@@ -26,15 +40,25 @@ TMP=$(mktemp -d)
TMP_ZIP=tmp.zip
BASE_DIR=$TMP/base
IMAGES_DIR=$TMP/images
OUTPUT_TMP_ZIP=$BASE_DIR/$TMP_ZIP
# determine executable extension
case `uname -s` in
*_NT-*) # for Windows
EXE=.exe
;;
*)
EXE=
;;
esac
unzip -q $BASE -d $BASE_DIR
unzip -q $IMAGES -d $IMAGES_DIR
BASE_DIR="$TMP"/base
IMAGES_DIR="$TMP"/images
OUTPUT_TMP_ZIP="$BASE_DIR/$TMP_ZIP"
UNZIPPED_BASE_DIR=$(echo $BASE_DIR/*)
UNZIPPED_IMAGE_DIR=$(echo $IMAGES_DIR/*)
unzip $Q "$BASE" -d "$BASE_DIR"
unzip $Q "$IMAGES" -d "$IMAGES_DIR"
UNZIPPED_BASE_DIR=$(echo "$BASE_DIR"/*)
UNZIPPED_IMAGE_DIR=$(echo "$IMAGES_DIR"/*)
#
# The commands to copy over the files that we want
@@ -42,21 +66,40 @@ UNZIPPED_IMAGE_DIR=$(echo $IMAGES_DIR/*)
# replace tools/emulator # at this time we do not want the exe from SDK1.x
replace tools/lib/images
replace tools/lib/res
replace tools/lib/fonts
replace tools/lib/layoutlib.jar
replace docs
replace android.jar
for i in widgets categories broadcast_actions service_actions; do
replace tools/lib/$i.txt
done
if [ -d "$UNZIPPED_BASE_DIR"/usb_driver ]; then
replace usb_driver
fi
#
# end
#
pushd $BASE_DIR &> /dev/null
if [ -z "$NOZIP" ]; then
pushd "$BASE_DIR" &> /dev/null
# rename the directory to the leaf minus the .zip of OUTPUT
LEAF=$(echo $OUTPUT | sed -e "s:.*\.zip/::" | sed -e "s:.zip$::")
mv * $LEAF
LEAF=$(echo "$OUTPUT" | sed -e "s:.*\.zip/::" | sed -e "s:.zip$::")
mv * "$LEAF"
# zip it
zip -qr $TMP_ZIP $LEAF
zip $V -qr "$TMP_ZIP" "$LEAF"
popd &> /dev/null
cp $OUTPUT_TMP_ZIP $OUTPUT
cp $V "$OUTPUT_TMP_ZIP" "$OUTPUT"
echo "Combined SDK available at $OUTPUT"
else
OUT_DIR="${OUTPUT//.zip/}"
mv $V "$BASE_DIR"/* "$OUT_DIR"
echo "Unzipped combined SDK available at $OUT_DIR"
fi
rm $V -rf "$TMP"
rm -rf $TMP

View File

@@ -31,15 +31,16 @@ import java.util.Map.Entry;
* <li>define flags for your actions.
* </ul>
* <p/>
* To use, call {@link #parseArgs(String[])} and then call {@link #getValue(String, String)}.
* To use, call {@link #parseArgs(String[])} and then
* call {@link #getValue(String, String, String)}.
*/
public class CommandLineProcessor {
/** Internal action name for all global flags. */
public final static String GLOBAL_FLAG = "global";
/** Internal action name for internally hidden flags.
* This is currently used to store the requested action name. */
public final static String INTERNAL_FLAG = "internal";
/** Internal verb name for internally hidden flags. */
public final static String GLOBAL_FLAG_VERB = "@@internal@@";
/** String to use when the verb doesn't need any object. */
public final static String NO_VERB_OBJECT = "";
/** The global help flag. */
public static final String KEY_HELP = "help";
@@ -47,36 +48,50 @@ public class CommandLineProcessor {
public static final String KEY_VERBOSE = "verbose";
/** The global silent flag. */
public static final String KEY_SILENT = "silent";
/** The internal action flag. */
public static final String KEY_ACTION = "action";
/** List of available actions.
/** Verb requested by the user. Null if none specified, which will be an error. */
private String mVerbRequested;
/** Direct object requested by the user. Can be null. */
private String mDirectObjectRequested;
/**
* Action definitions.
* <p/>
* Each entry must be a 2-string array with first the action name and then
* a description.
* Each entry is a string array with:
* <ul>
* <li> the verb.
* <li> a direct object (use #NO_VERB_OBJECT if there's no object).
* <li> a description.
* <li> an alternate form for the object (e.g. plural).
* </ul>
*/
private final String[][] mActions;
/** The hash of all defined arguments.
private static final int ACTION_VERB_INDEX = 0;
private static final int ACTION_OBJECT_INDEX = 1;
private static final int ACTION_DESC_INDEX = 2;
private static final int ACTION_ALT_OBJECT_INDEX = 3;
/**
* The map of all defined arguments.
* <p/>
* The key is a string "action/longName".
* The key is a string "verb/directObject/longName".
*/
private final HashMap<String, Arg> mArguments = new HashMap<String, Arg>();
/** Logger */
private final ISdkLog mLog;
public CommandLineProcessor(ISdkLog logger, String[][] actions) {
mLog = logger;
mActions = actions;
define(MODE.STRING, false, INTERNAL_FLAG, null, KEY_ACTION,
"Selected Action", null);
define(MODE.BOOLEAN, false, GLOBAL_FLAG, "v", KEY_VERBOSE,
define(MODE.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "v", KEY_VERBOSE,
"Verbose mode: errors, warnings and informational messages are printed.",
false);
define(MODE.BOOLEAN, false, GLOBAL_FLAG, "s", KEY_SILENT,
define(MODE.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "s", KEY_SILENT,
"Silent mode: only errors are printed out.",
false);
define(MODE.BOOLEAN, false, GLOBAL_FLAG, "h", KEY_HELP,
define(MODE.BOOLEAN, false, GLOBAL_FLAG_VERB, NO_VERB_OBJECT, "h", KEY_HELP,
"This help.",
false);
}
@@ -86,47 +101,54 @@ public class CommandLineProcessor {
/** Helper that returns true if --verbose was requested. */
public boolean isVerbose() {
return ((Boolean) getValue(GLOBAL_FLAG, KEY_VERBOSE)).booleanValue();
return ((Boolean) getValue(GLOBAL_FLAG_VERB, NO_VERB_OBJECT, KEY_VERBOSE)).booleanValue();
}
/** Helper that returns true if --silent was requested. */
public boolean isSilent() {
return ((Boolean) getValue(GLOBAL_FLAG, KEY_SILENT)).booleanValue();
return ((Boolean) getValue(GLOBAL_FLAG_VERB, NO_VERB_OBJECT, KEY_SILENT)).booleanValue();
}
/** Helper that returns true if --help was requested. */
public boolean isHelpRequested() {
return ((Boolean) getValue(GLOBAL_FLAG, KEY_HELP)).booleanValue();
return ((Boolean) getValue(GLOBAL_FLAG_VERB, NO_VERB_OBJECT, KEY_HELP)).booleanValue();
}
/** Helper that returns the requested action name. */
public String getActionRequested() {
return (String) getValue(INTERNAL_FLAG, KEY_ACTION);
/** Returns the verb name from the command-line. Can be null. */
public String getVerb() {
return mVerbRequested;
}
/** Returns the direct object name from the command-line. Can be null. */
public String getDirectObject() {
return mDirectObjectRequested;
}
//------------------
/**
* Raw access to parsed parameter values.
* @param action The action name, including {@link #GLOBAL_FLAG} and {@link #INTERNAL_FLAG}
* @param verb The verb name, including {@link #GLOBAL_FLAG_VERB}.
* @param directObject The direct object name, including {@link #NO_VERB_OBJECT}.
* @param longFlagName The long flag name for the given action.
* @return The current value object stored in the parameter, which depends on the argument mode.
*/
public Object getValue(String action, String longFlagName) {
String key = action + "/" + longFlagName;
public Object getValue(String verb, String directObject, String longFlagName) {
String key = verb + "/" + directObject + "/" + longFlagName;
Arg arg = mArguments.get(key);
return arg.getCurrentValue();
}
/**
* Internal setter for raw parameter value.
* @param action The action name, including {@link #GLOBAL_FLAG} and {@link #INTERNAL_FLAG}
* @param verb The verb name, including {@link #GLOBAL_FLAG_VERB}.
* @param directObject The direct object name, including {@link #NO_VERB_OBJECT}.
* @param longFlagName The long flag name for the given action.
* @param value The new current value object stored in the parameter, which depends on the
* argument mode.
*/
protected void setValue(String action, String longFlagName, Object value) {
String key = action + "/" + longFlagName;
protected void setValue(String verb, String directObject, String longFlagName, Object value) {
String key = verb + "/" + directObject + "/" + longFlagName;
Arg arg = mArguments.get(key);
arg.setCurrentValue(value);
}
@@ -140,56 +162,93 @@ public class CommandLineProcessor {
*/
public void parseArgs(String[] args) {
String needsHelp = null;
String action = null;
String verb = null;
String directObject = null;
try {
int n = args.length;
for (int i = 0; i < n; i++) {
Arg arg = null;
String a = args[i];
if (a.startsWith("--")) {
arg = findLongArg(action, a.substring(2));
arg = findLongArg(verb, directObject, a.substring(2));
} else if (a.startsWith("-")) {
arg = findShortArg(action, a.substring(1));
arg = findShortArg(verb, directObject, a.substring(1));
}
// Not a keyword and we don't have an action yet, this should be an action
if (arg == null && action == null) {
// No matching argument name found
if (arg == null) {
// Does it looks like a dashed parameter?
if (a.startsWith("-")) {
// Got a keyword but not valid for global flags
needsHelp = String.format(
"Flag '%1$s' is not a valid global flag. Did you mean to specify it after the action name?",
a, action);
break;
}
if (verb == null || directObject == null) {
// It looks like a dashed parameter and we don't have a a verb/object
// set yet, the parameter was just given too early.
for (String[] actionDesc : mActions) {
if (actionDesc[0].equals(a)) {
action = a;
break;
}
}
if (action == null) {
needsHelp = String.format(
"Expected action name after global parameters but found %1$s instead.",
"Flag '%1$s' is not a valid global flag. Did you mean to specify it after the verb/object name?",
a);
return;
} else {
// It looks like a dashed parameter and but it is unknown by this
// verb-object combination
needsHelp = String.format(
"Flag '%1$s' is not valid for '%2$s %3$s'.",
a, verb, directObject);
return;
}
}
if (verb == null) {
// Fill verb first. Find it.
for (String[] actionDesc : mActions) {
if (actionDesc[ACTION_VERB_INDEX].equals(a)) {
verb = a;
break;
}
} else if (arg == null && action != null) {
// Got a keyword but not valid for the current action
needsHelp = String.format(
"Flag '%1$s' is not valid for action '%2$s'.",
a, action);
break;
}
// Error if it was not a valid verb
if (verb == null) {
needsHelp = String.format(
"Expected verb after global parameters but found '%1$s' instead.",
a);
return;
}
} else if (directObject == null) {
// Then fill the direct object. Find it.
for (String[] actionDesc : mActions) {
if (actionDesc[ACTION_VERB_INDEX].equals(verb)) {
if (actionDesc[ACTION_OBJECT_INDEX].equals(a)) {
directObject = a;
break;
} else if (actionDesc.length > ACTION_ALT_OBJECT_INDEX &&
actionDesc[ACTION_ALT_OBJECT_INDEX].equals(a)) {
// if the alternate form exist and is used, we internally
// only memorize the default direct object form.
directObject = actionDesc[ACTION_OBJECT_INDEX];
break;
}
}
}
// Error if it was not a valid object for that verb
if (directObject == null) {
needsHelp = String.format(
"Expected verb after global parameters but found '%1$s' instead.",
a);
return;
}
}
} else if (arg != null) {
// Process keyword
String error = null;
if (arg.getMode().needsExtra()) {
if (++i >= n) {
needsHelp = String.format("Missing argument for flag %1$s.", a);
break;
return;
}
error = arg.getMode().process(arg, args[i]);
@@ -209,21 +268,38 @@ public class CommandLineProcessor {
if (error != null) {
needsHelp = String.format("Invalid usage for flag %1$s: %2$s.", a, error);
break;
return;
}
}
}
if (needsHelp == null) {
if (action == null) {
needsHelp = "Missing action name.";
if (verb == null) {
needsHelp = "Missing verb name.";
} else {
if (directObject == null) {
// Make sure this verb has an optional direct object
for (String[] actionDesc : mActions) {
if (actionDesc[ACTION_VERB_INDEX].equals(verb) &&
actionDesc[ACTION_OBJECT_INDEX].equals(NO_VERB_OBJECT)) {
directObject = NO_VERB_OBJECT;
break;
}
}
if (directObject == null) {
needsHelp = String.format("Missing object name for verb '%1$s'.", verb);
return;
}
}
// Validate that all mandatory arguments are non-null for this action
String missing = null;
boolean plural = false;
for (Entry<String, Arg> entry : mArguments.entrySet()) {
Arg arg = entry.getValue();
if (arg.getAction().equals(action)) {
if (arg.getVerb().equals(verb) &&
arg.getDirectObject().equals(directObject)) {
if (arg.isMandatory() && arg.getCurrentValue() == null) {
if (missing == null) {
missing = "--" + arg.getLongArg();
@@ -236,18 +312,22 @@ public class CommandLineProcessor {
}
if (missing != null) {
needsHelp = String.format("The %1$s %2$s must be defined for action '%3$s'",
needsHelp = String.format(
"The %1$s %2$s must be defined for action '%3$s %4$s'",
plural ? "parameters" : "parameter",
missing,
action);
verb,
directObject);
}
setValue(INTERNAL_FLAG, KEY_ACTION, action);
mVerbRequested = verb;
mDirectObjectRequested = directObject;
}
}
} finally {
if (needsHelp != null) {
printHelpAndExitForAction(action, needsHelp);
printHelpAndExitForAction(verb, directObject, needsHelp);
}
}
}
@@ -255,11 +335,14 @@ public class CommandLineProcessor {
* Finds an {@link Arg} given an action name and a long flag name.
* @return The {@link Arg} found or null.
*/
protected Arg findLongArg(String action, String longName) {
if (action == null) {
action = GLOBAL_FLAG;
protected Arg findLongArg(String verb, String directObject, String longName) {
if (verb == null) {
verb = GLOBAL_FLAG_VERB;
}
String key = action + "/" + longName;
if (directObject == null) {
directObject = NO_VERB_OBJECT;
}
String key = verb + "/" + directObject + "/" + longName;
return mArguments.get(key);
}
@@ -267,14 +350,17 @@ public class CommandLineProcessor {
* Finds an {@link Arg} given an action name and a short flag name.
* @return The {@link Arg} found or null.
*/
protected Arg findShortArg(String action, String shortName) {
if (action == null) {
action = GLOBAL_FLAG;
protected Arg findShortArg(String verb, String directObject, String shortName) {
if (verb == null) {
verb = GLOBAL_FLAG_VERB;
}
if (directObject == null) {
directObject = NO_VERB_OBJECT;
}
for (Entry<String, Arg> entry : mArguments.entrySet()) {
Arg arg = entry.getValue();
if (arg.getAction().equals(action)) {
if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) {
if (shortName.equals(arg.getShortArg())) {
return arg;
}
@@ -291,18 +377,22 @@ public class CommandLineProcessor {
* @param args Arguments for String.format
*/
public void printHelpAndExit(String errorFormat, Object... args) {
printHelpAndExitForAction(null /*actionFilter*/, errorFormat, args);
printHelpAndExitForAction(null /*verb*/, null /*directObject*/, errorFormat, args);
}
/**
* Prints the help/usage and exits.
*
* @param actionFilter If null, displays help for all actions. If not null, display help only
* for that specific action. In all cases also display general usage and action list.
* @param verb If null, displays help for all verbs. If not null, display help only
* for that specific verb. In all cases also displays general usage and action list.
* @param directObject If null, displays help for all verb objects.
* If not null, displays help only for that specific action
* In all cases also display general usage and action list.
* @param errorFormat Optional error message to print prior to usage using String.format
* @param args Arguments for String.format
*/
public void printHelpAndExitForAction(String actionFilter, String errorFormat, Object... args) {
public void printHelpAndExitForAction(String verb, String directObject,
String errorFormat, Object... args) {
if (errorFormat != null) {
stderr(errorFormat, args);
}
@@ -316,25 +406,27 @@ public class CommandLineProcessor {
" android [global options] action [action options]\n" +
"\n" +
"Global options:");
listOptions(GLOBAL_FLAG);
listOptions(GLOBAL_FLAG_VERB, NO_VERB_OBJECT);
stdout("\nValid actions:");
stdout("\nValid actions are composed of a verb and an optional direct object:");
for (String[] action : mActions) {
String filler = "";
int len = action[0].length();
if (len < 10) {
filler = " ".substring(len);
}
stdout("- %1$s:%2$s %3$s", action[0], filler, action[1]);
stdout("- %1$6s %2$-7s: %3$s",
action[ACTION_VERB_INDEX],
action[ACTION_OBJECT_INDEX],
action[ACTION_DESC_INDEX]);
}
for (String[] action : mActions) {
if (actionFilter == null || actionFilter.equals(action[0])) {
stdout("\nAction \"%1$s\":", action[0]);
stdout(" %1$s", action[1]);
if (verb == null || verb.equals(action[ACTION_VERB_INDEX])) {
if (directObject == null || directObject.equals(action[ACTION_OBJECT_INDEX])) {
stdout("\nAction \"%1$s %2$s\":",
action[ACTION_VERB_INDEX],
action[ACTION_OBJECT_INDEX]);
stdout(" %1$s", action[ACTION_DESC_INDEX]);
stdout("Options:");
listOptions(action[0]);
listOptions(action[ACTION_VERB_INDEX], action[ACTION_OBJECT_INDEX]);
}
}
}
@@ -344,11 +436,11 @@ public class CommandLineProcessor {
/**
* Internal helper to print all the option flags for a given action name.
*/
protected void listOptions(String action) {
protected void listOptions(String verb, String directObject) {
int numOptions = 0;
for (Entry<String, Arg> entry : mArguments.entrySet()) {
Arg arg = entry.getValue();
if (arg.getAction().equals(action)) {
if (arg.getVerb().equals(verb) && arg.getDirectObject().equals(directObject)) {
String value = "";
if (arg.getDefaultValue() instanceof String[]) {
@@ -483,21 +575,22 @@ public class CommandLineProcessor {
* or a String array (in which case the first item is the current by default.)
*/
static class Arg {
private final String mAction;
private final String mVerb;
private final String mDirectObject;
private final String mShortName;
private final String mLongName;
private final String mDescription;
private final Object mDefaultValue;
private Object mCurrentValue;
private final MODE mMode;
private final boolean mMandatory;
private Object mCurrentValue;
/**
* Creates a new argument flag description.
*
* @param mode The {@link MODE} for the argument.
* @param mandatory True if this argument is mandatory for this action.
* @param action The action name. Can be #GLOBAL_FLAG or #INTERNAL_FLAG.
* @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG.
* @param shortName The one-letter short argument name. Cannot be empty nor null.
* @param longName The long argument name. Cannot be empty nor null.
* @param description The description. Cannot be null.
@@ -505,14 +598,16 @@ public class CommandLineProcessor {
*/
public Arg(MODE mode,
boolean mandatory,
String action,
String verb,
String directObject,
String shortName,
String longName,
String description,
Object defaultValue) {
mMode = mode;
mMandatory = mandatory;
mAction = action;
mVerb = verb;
mDirectObject = directObject;
mShortName = shortName;
mLongName = longName;
mDescription = description;
@@ -540,8 +635,12 @@ public class CommandLineProcessor {
return mDescription;
}
public String getAction() {
return mAction;
public String getVerb() {
return mVerb;
}
public String getDirectObject() {
return mDirectObject;
}
public Object getDefaultValue() {
@@ -565,7 +664,8 @@ public class CommandLineProcessor {
* Internal helper to define a new argument for a give action.
*
* @param mode The {@link MODE} for the argument.
* @param action The action name. Can be #GLOBAL_FLAG or #INTERNAL_FLAG.
* @param verb The verb name. Can be #INTERNAL_VERB.
* @param directObject The action name. Can be #NO_VERB_OBJECT or #INTERNAL_FLAG.
* @param shortName The one-letter short argument name. Cannot be empty nor null.
* @param longName The long argument name. Cannot be empty nor null.
* @param description The description. Cannot be null.
@@ -573,14 +673,19 @@ public class CommandLineProcessor {
*/
protected void define(MODE mode,
boolean mandatory,
String action,
String verb,
String directObject,
String shortName, String longName,
String description, Object defaultValue) {
assert(mandatory || mode == MODE.BOOLEAN); // a boolean mode cannot be mandatory
String key = action + "/" + longName;
if (directObject == null) {
directObject = NO_VERB_OBJECT;
}
String key = verb + "/" + directObject + "/" + longName;
mArguments.put(key, new Arg(mode, mandatory,
action, shortName, longName, description, defaultValue));
verb, directObject, shortName, longName, description, defaultValue));
}
/**

View File

@@ -23,12 +23,12 @@ import com.android.sdklib.ISdkLog;
import com.android.sdklib.SdkConstants;
import com.android.sdklib.SdkManager;
import com.android.sdklib.IAndroidTarget.IOptionalLibrary;
import com.android.sdklib.avd.AvdManager;
import com.android.sdklib.avd.HardwareProperties;
import com.android.sdklib.avd.AvdManager.AvdInfo;
import com.android.sdklib.avd.HardwareProperties.HardwareProperty;
import com.android.sdklib.project.ProjectCreator;
import com.android.sdklib.project.ProjectCreator.OutputLevel;
import com.android.sdklib.vm.HardwareProperties;
import com.android.sdklib.vm.VmManager;
import com.android.sdklib.vm.HardwareProperties.HardwareProperty;
import com.android.sdklib.vm.VmManager.VmInfo;
import java.io.File;
import java.io.IOException;
@@ -56,8 +56,8 @@ class Main {
private ISdkLog mSdkLog;
/** The SDK manager parses the SDK folder and gives access to the content. */
private SdkManager mSdkManager;
/** Virtual Machine manager to access the list of VMs or create new ones. */
private VmManager mVmManager;
/** Virtual Machine manager to access the list of AVDs or create new ones. */
private AvdManager mAvdManager;
/** Command-line processor with options specific to SdkManager. */
private SdkCommandLine mSdkCommandLine;
/** The working directory, either null or set to an existing absolute canonical directory. */
@@ -183,26 +183,31 @@ class Main {
* Actually do an action...
*/
private void doAction() {
String action = mSdkCommandLine.getActionRequested();
String verb = mSdkCommandLine.getVerb();
String directObject = mSdkCommandLine.getDirectObject();
if (SdkCommandLine.ACTION_LIST.equals(action)) {
if (SdkCommandLine.VERB_LIST.equals(verb)) {
// list action.
if (SdkCommandLine.ARG_TARGET.equals(mSdkCommandLine.getListFilter())) {
if (SdkCommandLine.OBJECT_TARGET.equals(directObject)) {
displayTargetList();
} else if (SdkCommandLine.ARG_VM.equals(mSdkCommandLine.getListFilter())) {
displayVmList();
} else if (SdkCommandLine.OBJECT_AVD.equals(directObject)) {
displayAvdList();
} else {
displayTargetList();
displayVmList();
displayAvdList();
}
} else if (SdkCommandLine.ACTION_NEW_VM.equals(action)) {
createVm();
} else if (SdkCommandLine.ACTION_NEW_PROJECT.equals(action)) {
} else if (SdkCommandLine.VERB_CREATE.equals(verb) &&
SdkCommandLine.OBJECT_AVD.equals(directObject)) {
createAvd();
} else if (SdkCommandLine.VERB_CREATE.equals(verb) &&
SdkCommandLine.OBJECT_PROJECT.equals(directObject)) {
// get the target and try to resolve it.
int targetId = mSdkCommandLine.getNewProjectTargetId();
int targetId = mSdkCommandLine.getCreateProjectTargetId();
IAndroidTarget[] targets = mSdkManager.getTargets();
if (targetId < 1 || targetId > targets.length) {
errorAndExit("Target id is not valid. Use '%s list -f target' to get the target Ids.",
errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.",
SdkConstants.androidCmdName());
}
IAndroidTarget target = targets[targetId - 1];
@@ -213,22 +218,24 @@ class Main {
OutputLevel.NORMAL,
mSdkLog);
String projectDir = getProjectLocation(mSdkCommandLine.getNewProjectLocation());
String projectDir = getProjectLocation(mSdkCommandLine.getCreateProjectLocation());
creator.createProject(projectDir,
mSdkCommandLine.getNewProjectName(),
mSdkCommandLine.getNewProjectPackage(),
mSdkCommandLine.getNewProjectActivity(),
mSdkCommandLine.getCreateProjectName(),
mSdkCommandLine.getCreateProjectPackage(),
mSdkCommandLine.getCreateProjectActivity(),
target,
false /* isTestProject*/);
} else if (SdkCommandLine.ACTION_UPDATE_PROJECT.equals(action)) {
} else if (SdkCommandLine.VERB_UPDATE.equals(verb) &&
SdkCommandLine.OBJECT_PROJECT.equals(directObject)) {
// get the target and try to resolve it.
IAndroidTarget target = null;
int targetId = mSdkCommandLine.getUpdateProjectTargetId();
if (targetId >= 0) {
IAndroidTarget[] targets = mSdkManager.getTargets();
if (targetId < 1 || targetId > targets.length) {
errorAndExit("Target id is not valid. Use '%s list -f target' to get the target Ids.",
errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.",
SdkConstants.androidCmdName());
}
target = targets[targetId - 1];
@@ -340,20 +347,20 @@ class Main {
}
/**
* Displays the list of available VMs.
* Displays the list of available AVDs.
*/
private void displayVmList() {
private void displayAvdList() {
try {
mVmManager = new VmManager(mSdkManager, null /* sdklog */);
mAvdManager = new AvdManager(mSdkManager, null /* sdklog */);
mSdkLog.printf("Available Android VMs:\n");
mSdkLog.printf("Available Android Virtual Devices:\n");
int index = 1;
for (VmInfo info : mVmManager.getVms()) {
for (AvdInfo info : mAvdManager.getAvds()) {
mSdkLog.printf("[%d] %s\n", index, info.getName());
mSdkLog.printf(" Path: %s\n", info.getPath());
// get the target of the Vm
// get the target of the AVD
IAndroidTarget target = info.getTarget();
if (target.isPlatform()) {
mSdkLog.printf(" Target: %s (API level %d)\n", target.getName(),
@@ -373,31 +380,41 @@ class Main {
}
/**
* Creates a new VM. This is a text based creation with command line prompt.
* Creates a new AVD. This is a text based creation with command line prompt.
*/
private void createVm() {
private void createAvd() {
// find a matching target
int targetId = mSdkCommandLine.getNewVmTargetId();
int targetId = mSdkCommandLine.getCreateAvdTargetId();
IAndroidTarget target = null;
if (targetId >= 1 && targetId <= mSdkManager.getTargets().length) {
target = mSdkManager.getTargets()[targetId-1]; // target it is 1-based
} else {
errorAndExit("Target id is not valid. Use '%s list -f target' to get the target Ids.",
errorAndExit("Target id is not valid. Use '%s list targets' to get the target ids.",
SdkConstants.androidCmdName());
}
try {
mVmManager = new VmManager(mSdkManager, mSdkLog);
boolean removePrevious = false;
mAvdManager = new AvdManager(mSdkManager, mSdkLog);
String vmName = mSdkCommandLine.getNewVmName();
VmInfo info = mVmManager.getVm(vmName);
String avdName = mSdkCommandLine.getCreateAvdName();
AvdInfo info = mAvdManager.getAvd(avdName);
if (info != null) {
errorAndExit("VM %s already exists.", vmName);
if (mSdkCommandLine.getCreateAvdForce()) {
removePrevious = true;
mSdkLog.warning(
"Android Virtual Device '%s' already exists and will be replaced.",
avdName);
} else {
String vmParentFolder = mSdkCommandLine.getNewVmLocation();
if (vmParentFolder == null) {
vmParentFolder = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS;
errorAndExit("Android Virtual Device '%s' already exists.", avdName);
return;
}
}
String avdParentFolder = mSdkCommandLine.getCreateAvdLocation();
if (avdParentFolder == null) {
avdParentFolder = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
}
Map<String, String> hardwareConfig = null;
@@ -409,21 +426,39 @@ class Main {
}
}
mVmManager.createVm(vmParentFolder,
mSdkCommandLine.getNewVmName(),
target,
mSdkCommandLine.getNewVmSkin(),
mSdkCommandLine.getNewVmSdCard(),
hardwareConfig,
mSdkLog);
AvdInfo oldAvdInfo = null;
if (removePrevious) {
oldAvdInfo = mAvdManager.getAvd(avdName);
}
AvdInfo newAvdInfo = mAvdManager.createAvd(avdParentFolder,
avdName,
target,
mSdkCommandLine.getCreateAvdSkin(),
mSdkCommandLine.getCreateAvdSdCard(),
hardwareConfig,
removePrevious,
mSdkLog);
if (newAvdInfo != null &&
oldAvdInfo != null &&
!oldAvdInfo.getPath().equals(newAvdInfo.getPath())) {
mSdkLog.warning("Removing previous AVD directory at %s", oldAvdInfo.getPath());
// Remove the old data directory
File dir = new File(oldAvdInfo.getPath());
mAvdManager.recursiveDelete(dir);
dir.delete();
// Remove old avd info from manager
mAvdManager.removeAvd(oldAvdInfo);
}
} catch (AndroidLocationException e) {
errorAndExit(e.getMessage());
}
}
/**
* Prompts the user to setup a hardware config for a Platform-based VM.
* Prompts the user to setup a hardware config for a Platform-based AVD.
* @throws IOException
*/
private Map<String, String> promptForHardware(IAndroidTarget createTarget) throws IOException {

View File

@@ -25,152 +25,218 @@ import com.android.sdklib.SdkManager;
*/
public class SdkCommandLine extends CommandLineProcessor {
public final static String VERB_LIST = "list";
public final static String VERB_CREATE = "create";
public final static String VERB_RENAME = "rename";
public final static String VERB_MOVE = "move";
public final static String VERB_DELETE = "delete";
public final static String VERB_UPDATE = "update";
public static final String OBJECT_AVD = "avd";
public static final String OBJECT_AVDS = "avds";
public static final String OBJECT_TARGET = "target";
public static final String OBJECT_TARGETS = "targets";
public static final String OBJECT_PROJECT = "project";
public static final String ARG_ALIAS = "alias";
public static final String ARG_ACTIVITY = "activity";
public static final String ARG_VM = "vm";
public static final String ARG_TARGET = "target";
public static final String ARG_ALL = "all";
public static final String KEY_ACTIVITY = ARG_ACTIVITY;
public static final String KEY_PACKAGE = "package";
public static final String KEY_MODE = "mode";
public static final String KEY_TARGET_ID = ARG_TARGET;
public static final String KEY_TARGET_ID = OBJECT_TARGET;
public static final String KEY_NAME = "name";
public static final String KEY_OUT = "out";
public static final String KEY_PATH = "path";
public static final String KEY_FILTER = "filter";
public static final String KEY_SKIN = "skin";
public static final String KEY_SDCARD = "sdcard";
public static final String KEY_FORCE = "force";
public final static String ACTION_LIST = "list";
public final static String ACTION_NEW_VM = ARG_VM;
public final static String ACTION_NEW_PROJECT = "project";
public final static String ACTION_UPDATE_PROJECT = "update";
/**
* Action definitions for SdkManager command line.
* <p/>
* Each entry is a string array with:
* <ul>
* <li> the verb.
* <li> an object (use #NO_VERB_OBJECT if there's no object).
* <li> a description.
* <li> an alternate form for the object (e.g. plural).
* </ul>
*/
private final static String[][] ACTIONS = {
{ ACTION_LIST,
"Lists existing targets or VMs." },
{ ACTION_NEW_VM,
"Creates a new VM." },
{ ACTION_NEW_PROJECT,
"Creates a new project using a template." },
{ ACTION_UPDATE_PROJECT,
"Updates a project from existing source (must have an AndroidManifest.xml)." },
{ VERB_LIST,
NO_VERB_OBJECT,
"Lists existing targets or virtual devices." },
{ VERB_LIST,
OBJECT_AVD,
"Lists existing Android Virtual Devices.",
OBJECT_AVDS },
{ VERB_LIST,
OBJECT_TARGET,
"Lists existing targets.",
OBJECT_TARGETS },
{ VERB_CREATE,
OBJECT_AVD,
"Creates a new Android Virtual Device." },
{ VERB_RENAME,
OBJECT_AVD,
"Renames a new Android Virtual Device." },
{ VERB_MOVE,
OBJECT_AVD,
"Moves a new Android Virtual Device." },
{ VERB_DELETE,
OBJECT_AVD,
"Deletes a new Android Virtual Device." },
{ VERB_CREATE,
OBJECT_PROJECT,
"Creates a new Android Project." },
{ VERB_UPDATE,
OBJECT_PROJECT,
"Updates an Android Project (must have an AndroidManifest.xml)." },
};
public SdkCommandLine(ISdkLog logger) {
super(logger, ACTIONS);
define(MODE.ENUM, false, ACTION_LIST, "f", KEY_FILTER,
"List filter", new String[] { ARG_ALL, ARG_TARGET, ARG_VM });
define(MODE.STRING, false,
VERB_CREATE, OBJECT_AVD,
"p", KEY_PATH,
"Location path of the parent directory where the new AVD will be created", null);
define(MODE.STRING, true,
VERB_CREATE, OBJECT_AVD,
"n", KEY_NAME,
"Name of the new AVD", null);
define(MODE.INTEGER, true,
VERB_CREATE, OBJECT_AVD,
"t", KEY_TARGET_ID,
"Target id of the new AVD", null);
define(MODE.STRING, true,
VERB_CREATE, OBJECT_AVD,
"s", KEY_SKIN,
"Skin of the new AVD", null);
define(MODE.STRING, false,
VERB_CREATE, OBJECT_AVD,
"c", KEY_SDCARD,
"Path to a shared SD card image, or size of a new sdcard for the new AVD", null);
define(MODE.BOOLEAN, false,
VERB_CREATE, OBJECT_AVD,
"f", KEY_FORCE,
"Force creation (override an existing AVD)", false);
define(MODE.STRING, false, ACTION_NEW_VM, "o", KEY_OUT,
"Location path of new VM", null);
define(MODE.STRING, true, ACTION_NEW_VM, "n", KEY_NAME,
"Name of the new VM", null);
define(MODE.INTEGER, true, ACTION_NEW_VM, "t", KEY_TARGET_ID,
"Target id of the new VM", null);
define(MODE.STRING, true, ACTION_NEW_VM, "s", KEY_SKIN,
"Skin of the new VM", null);
define(MODE.STRING, false, ACTION_NEW_VM, "c", KEY_SDCARD,
"Path to a shared SD card image, or size of a new sdcard for the new VM", null);
define(MODE.ENUM, true, ACTION_NEW_PROJECT, "m", KEY_MODE,
define(MODE.ENUM, true,
VERB_CREATE, OBJECT_PROJECT,
"m", KEY_MODE,
"Project mode", new String[] { ARG_ACTIVITY, ARG_ALIAS });
define(MODE.STRING, true, ACTION_NEW_PROJECT, "o", KEY_OUT,
define(MODE.STRING, true,
VERB_CREATE, OBJECT_PROJECT,
"p", KEY_PATH,
"Location path of new project", null);
define(MODE.INTEGER, true, ACTION_NEW_PROJECT, "t", KEY_TARGET_ID,
define(MODE.INTEGER, true,
VERB_CREATE, OBJECT_PROJECT,
"t", KEY_TARGET_ID,
"Target id of the new project", null);
define(MODE.STRING, true, ACTION_NEW_PROJECT, "p", KEY_PACKAGE,
define(MODE.STRING, true,
VERB_CREATE, OBJECT_PROJECT,
"k", KEY_PACKAGE,
"Package name", null);
define(MODE.STRING, true, ACTION_NEW_PROJECT, "a", KEY_ACTIVITY,
define(MODE.STRING, true,
VERB_CREATE, OBJECT_PROJECT,
"a", KEY_ACTIVITY,
"Activity name", null);
define(MODE.STRING, false, ACTION_NEW_PROJECT, "n", KEY_NAME,
define(MODE.STRING, false,
VERB_CREATE, OBJECT_PROJECT,
"n", KEY_NAME,
"Project name", null);
define(MODE.STRING, true, ACTION_UPDATE_PROJECT, "o", KEY_OUT,
define(MODE.STRING, true,
VERB_UPDATE, OBJECT_PROJECT,
"p", KEY_PATH,
"Location path of the project", null);
define(MODE.INTEGER, true, ACTION_UPDATE_PROJECT, "t", KEY_TARGET_ID,
define(MODE.INTEGER, true,
VERB_UPDATE, OBJECT_PROJECT,
"t", KEY_TARGET_ID,
"Target id to set for the project", -1);
define(MODE.STRING, false, ACTION_UPDATE_PROJECT, "n", KEY_NAME,
define(MODE.STRING, false,
VERB_UPDATE, OBJECT_PROJECT,
"n", KEY_NAME,
"Project name", null);
}
// -- some helpers for list action flags
// -- some helpers for AVD action flags
/** Helper to retrieve the --filter for the list action. */
public String getListFilter() {
return ((String) getValue(ACTION_LIST, KEY_FILTER));
/** Helper to retrieve the --out location for the new AVD action. */
public String getCreateAvdLocation() {
return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_PATH));
}
// -- some helpers for vm action flags
/** Helper to retrieve the --out location for the new vm action. */
public String getNewVmLocation() {
return ((String) getValue(ACTION_NEW_VM, KEY_OUT));
/** Helper to retrieve the --target id for the new AVD action. */
public int getCreateAvdTargetId() {
return ((Integer) getValue(VERB_CREATE, OBJECT_AVD, KEY_TARGET_ID)).intValue();
}
/** Helper to retrieve the --target id for the new vm action. */
public int getNewVmTargetId() {
return ((Integer) getValue(ACTION_NEW_VM, KEY_TARGET_ID)).intValue();
/** Helper to retrieve the --name for the new AVD action. */
public String getCreateAvdName() {
return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_NAME));
}
/** Helper to retrieve the --name for the new vm action. */
public String getNewVmName() {
return ((String) getValue(ACTION_NEW_VM, KEY_NAME));
/** Helper to retrieve the --skin name for the new AVD action. */
public String getCreateAvdSkin() {
return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_SKIN));
}
/** Helper to retrieve the --skin name for the new vm action. */
public String getNewVmSkin() {
return ((String) getValue(ACTION_NEW_VM, KEY_SKIN));
/** Helper to retrieve the --sdcard data for the new AVD action. */
public String getCreateAvdSdCard() {
return ((String) getValue(VERB_CREATE, OBJECT_AVD, KEY_SDCARD));
}
/** Helper to retrieve the --sdcard data for the new vm action. */
public String getNewVmSdCard() {
return ((String) getValue(ACTION_NEW_VM, KEY_SDCARD));
public boolean getCreateAvdForce() {
return ((Boolean) getValue(VERB_CREATE, OBJECT_AVD, KEY_FORCE)).booleanValue();
}
// -- some helpers for project action flags
/** Helper to retrieve the --out location for the new project action. */
public String getNewProjectLocation() {
return ((String) getValue(ACTION_NEW_PROJECT, KEY_OUT));
public String getCreateProjectLocation() {
return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_PATH));
}
/** Helper to retrieve the --target id for the new project action. */
public int getNewProjectTargetId() {
return ((Integer) getValue(ACTION_NEW_PROJECT, KEY_TARGET_ID)).intValue();
public int getCreateProjectTargetId() {
return ((Integer) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_TARGET_ID)).intValue();
}
/** Helper to retrieve the --name for the new project action. */
public String getNewProjectName() {
return ((String) getValue(ACTION_NEW_PROJECT, KEY_NAME));
public String getCreateProjectName() {
return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_NAME));
}
/** Helper to retrieve the --package for the new project action. */
public String getNewProjectPackage() {
return ((String) getValue(ACTION_NEW_PROJECT, KEY_PACKAGE));
public String getCreateProjectPackage() {
return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_PACKAGE));
}
/** Helper to retrieve the --activity for the new project action. */
public String getNewProjectActivity() {
return ((String) getValue(ACTION_NEW_PROJECT, KEY_ACTIVITY));
public String getCreateProjectActivity() {
return ((String) getValue(VERB_CREATE, OBJECT_PROJECT, KEY_ACTIVITY));
}
// -- some helpers for update action flags
/** Helper to retrieve the --out location for the update project action. */
public String getUpdateProjectLocation() {
return ((String) getValue(ACTION_UPDATE_PROJECT, KEY_OUT));
return ((String) getValue(VERB_UPDATE, OBJECT_PROJECT, KEY_PATH));
}
/** Helper to retrieve the --target id for the update project action. */
public int getUpdateProjectTargetId() {
return ((Integer) getValue(ACTION_UPDATE_PROJECT, KEY_TARGET_ID)).intValue();
return ((Integer) getValue(VERB_UPDATE, OBJECT_PROJECT, KEY_TARGET_ID)).intValue();
}
/** Helper to retrieve the --name for the update project action. */
public String getUpdateProjectName() {
return ((String) getValue(ACTION_UPDATE_PROJECT, KEY_NAME));
return ((String) getValue(VERB_UPDATE, OBJECT_PROJECT, KEY_NAME));
}
}

View File

@@ -38,20 +38,20 @@ public class CommandLineProcessorTest extends TestCase {
public MockCommandLineProcessor(ISdkLog logger) {
super(logger,
new String[][] {
{ "action1", "Some action" },
{ "action2", "Another action" },
{ "verb1", "action1", "Some action" },
{ "verb1", "action2", "Another action" },
});
define(MODE.STRING, false /*mandatory*/,
"action1", "1", "first", "non-mandatory flag", null);
"verb1", "action1", "1", "first", "non-mandatory flag", null);
define(MODE.STRING, true /*mandatory*/,
"action1", "2", "second", "mandatory flag", null);
"verb1", "action1", "2", "second", "mandatory flag", null);
}
@Override
public void printHelpAndExitForAction(String actionFilter,
public void printHelpAndExitForAction(String verb, String directObject,
String errorFormat, Object... args) {
mHelpCalled = true;
super.printHelpAndExitForAction(actionFilter, errorFormat, args);
super.printHelpAndExitForAction(verb, directObject, errorFormat, args);
}
@Override
@@ -132,14 +132,14 @@ public class CommandLineProcessorTest extends TestCase {
assertTrue(c.isVerbose());
assertTrue(c.wasExitCalled());
assertTrue(c.wasHelpCalled());
assertTrue(c.getStdErr().indexOf("Missing action name.") != -1);
assertTrue(c.getStdErr().indexOf("Missing verb name.") != -1);
c = new MockCommandLineProcessor(mLog);
c.parseArgs(new String[] { "--verbose" });
assertTrue(c.isVerbose());
assertTrue(c.wasExitCalled());
assertTrue(c.wasHelpCalled());
assertTrue(c.getStdErr().indexOf("Missing action name.") != -1);
assertTrue(c.getStdErr().indexOf("Missing verb name.") != -1);
}
public final void testHelp() {
@@ -148,39 +148,39 @@ public class CommandLineProcessorTest extends TestCase {
c.parseArgs(new String[] { "-h" });
assertTrue(c.wasExitCalled());
assertTrue(c.wasHelpCalled());
assertTrue(c.getStdErr().indexOf("Missing action name.") == -1);
assertTrue(c.getStdErr().indexOf("Missing verb name.") == -1);
c = new MockCommandLineProcessor(mLog);
c.parseArgs(new String[] { "--help" });
assertTrue(c.wasExitCalled());
assertTrue(c.wasHelpCalled());
assertTrue(c.getStdErr().indexOf("Missing action name.") == -1);
assertTrue(c.getStdErr().indexOf("Missing verb name.") == -1);
}
public final void testMandatory() {
MockCommandLineProcessor c = new MockCommandLineProcessor(mLog);
c.parseArgs(new String[] { "action1", "-1", "value1", "-2", "value2" });
c.parseArgs(new String[] { "verb1", "action1", "-1", "value1", "-2", "value2" });
assertFalse(c.wasExitCalled());
assertFalse(c.wasHelpCalled());
assertEquals("", c.getStdErr());
assertEquals("value1", c.getValue("action1", "first"));
assertEquals("value2", c.getValue("action1", "second"));
assertEquals("value1", c.getValue("verb1", "action1", "first"));
assertEquals("value2", c.getValue("verb1", "action1", "second"));
c = new MockCommandLineProcessor(mLog);
c.parseArgs(new String[] { "action1", "-2", "value2" });
c.parseArgs(new String[] { "verb1", "action1", "-2", "value2" });
assertFalse(c.wasExitCalled());
assertFalse(c.wasHelpCalled());
assertEquals("", c.getStdErr());
assertEquals(null, c.getValue("action1", "first"));
assertEquals("value2", c.getValue("action1", "second"));
assertEquals(null, c.getValue("verb1", "action1", "first"));
assertEquals("value2", c.getValue("verb1", "action1", "second"));
c = new MockCommandLineProcessor(mLog);
c.parseArgs(new String[] { "action1" });
c.parseArgs(new String[] { "verb1", "action1" });
assertTrue(c.wasExitCalled());
assertTrue(c.wasHelpCalled());
assertTrue(c.getStdErr().indexOf("must be defined") != -1);
assertEquals(null, c.getValue("action1", "first"));
assertEquals(null, c.getValue("action1", "second"));
assertEquals(null, c.getValue("verb1", "action1", "first"));
assertEquals(null, c.getValue("verb1", "action1", "second"));
}
}

View File

@@ -37,10 +37,10 @@ public class SdkCommandLineTest extends TestCase {
}
@Override
public void printHelpAndExitForAction(String actionFilter,
public void printHelpAndExitForAction(String verb, String directObject,
String errorFormat, Object... args) {
mHelpCalled = true;
super.printHelpAndExitForAction(actionFilter, errorFormat, args);
super.printHelpAndExitForAction(verb, directObject, errorFormat, args);
}
@Override
@@ -78,34 +78,64 @@ public class SdkCommandLineTest extends TestCase {
super.tearDown();
}
/** Test list with long name and verbose */
public final void testList_Long_Verbose() {
/** Test list */
public final void testList_Avd_Verbose() {
MockSdkCommandLine c = new MockSdkCommandLine(mLog);
assertEquals("all", c.getListFilter());
c.parseArgs(new String[] { "-v", "list", "--filter", "vm" });
c.parseArgs(new String[] { "-v", "list", "avd" });
assertFalse(c.wasHelpCalled());
assertFalse(c.wasExitCalled());
assertEquals("vm", c.getListFilter());
assertEquals("list", c.getVerb());
assertEquals("avd", c.getDirectObject());
assertTrue(c.isVerbose());
}
/** Test list with short name and no verbose */
public final void testList_Short() {
public final void testList_Target() {
MockSdkCommandLine c = new MockSdkCommandLine(mLog);
assertEquals("all", c.getListFilter());
c.parseArgs(new String[] { "list", "-f", "vm" });
c.parseArgs(new String[] { "list", "target" });
assertFalse(c.wasHelpCalled());
assertFalse(c.wasExitCalled());
assertEquals("vm", c.getListFilter());
assertEquals("list", c.getVerb());
assertEquals("target", c.getDirectObject());
assertFalse(c.isVerbose());
}
/** Test list with long name and missing parameter */
public final void testList_Long_MissingParam() {
public final void testList_None() {
MockSdkCommandLine c = new MockSdkCommandLine(mLog);
assertEquals("all", c.getListFilter());
c.parseArgs(new String[] { "list", "--filter" });
c.parseArgs(new String[] { "list" });
assertFalse(c.wasHelpCalled());
assertFalse(c.wasExitCalled());
assertEquals("list", c.getVerb());
assertEquals("", c.getDirectObject());
assertFalse(c.isVerbose());
}
public final void testList_Invalid() {
MockSdkCommandLine c = new MockSdkCommandLine(mLog);
c.parseArgs(new String[] { "list", "unknown" });
assertTrue(c.wasHelpCalled());
assertTrue(c.wasExitCalled());
assertEquals("all", c.getListFilter());
assertEquals(null, c.getVerb());
assertEquals(null, c.getDirectObject());
assertFalse(c.isVerbose());
}
public final void testList_Plural() {
MockSdkCommandLine c = new MockSdkCommandLine(mLog);
c.parseArgs(new String[] { "list", "avds" });
assertFalse(c.wasHelpCalled());
assertFalse(c.wasExitCalled());
assertEquals("list", c.getVerb());
// we get the non-plural form
assertEquals("avd", c.getDirectObject());
assertFalse(c.isVerbose());
c = new MockSdkCommandLine(mLog);
c.parseArgs(new String[] { "list", "targets" });
assertFalse(c.wasHelpCalled());
assertFalse(c.wasExitCalled());
assertEquals("list", c.getVerb());
// we get the non-plural form
assertEquals("target", c.getDirectObject());
assertFalse(c.isVerbose());
}
}

View File

@@ -16,11 +16,48 @@
package com.android.sdklib;
import java.util.Formatter;
/**
* Interface used to display warnings/errors while parsing the SDK content.
*/
public interface ISdkLog {
/**
* Prints a warning message on stdout.
* <p/>
* Implementations should only display warnings in verbose mode.
* The message should be prefixed with "Warning:".
*
* @param warningFormat is an optional error format. If non-null, it will be printed
* using a {@link Formatter} with the provided arguments.
* @param args provides the arguments for warningFormat.
*/
void warning(String warningFormat, Object... args);
/**
* Prints an error message on stderr.
* <p/>
* Implementation should always display errors, independent of verbose mode.
* The message should be prefixed with "Error:".
*
* @param t is an optional {@link Throwable} or {@link Exception}. If non-null, it's
* message will be printed out.
* @param errorFormat is an optional error format. If non-null, it will be printed
* using a {@link Formatter} with the provided arguments.
* @param args provides the arguments for errorFormat.
*/
void error(Throwable t, String errorFormat, Object... args);
/**
* Prints a message as-is on stdout.
* <p/>
* Implementation should always display errors, independent of verbose mode.
* No prefix is used, the message is printed as-is after formatting.
*
* @param msgFormat is an optional error format. If non-null, it will be printed
* using a {@link Formatter} with the provided arguments.
* @param args provides the arguments for msgFormat.
*/
void printf(String msgFormat, Object... args);
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.sdklib.vm;
package com.android.sdklib.avd;
import com.android.prefs.AndroidLocation;
import com.android.prefs.AndroidLocation.AndroidLocationException;
@@ -39,12 +39,13 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Virtual Machine manager to access the list of VMs or create new ones.
* Virtual Device Manager to access the list of AVDs or create new ones.
*/
public final class VmManager {
public final class AvdManager {
private final static String VM_INFO_PATH = "path";
private final static String VM_INFO_TARGET = "target";
private static final String AVD_FOLDER_EXTENSION = ".avd";
private final static String AVD_INFO_PATH = "path";
private final static String AVD_INFO_TARGET = "target";
private final static String IMAGE_USERDATA = "userdata.img";
private final static String CONFIG_INI = "config.ini";
@@ -54,7 +55,7 @@ public final class VmManager {
private final static Pattern SDCARD_SIZE_PATTERN = Pattern.compile("\\d+[MK]?");
public static final class VmInfo {
public static final class AvdInfo {
String name;
String path;
IAndroidTarget target;
@@ -72,30 +73,30 @@ public final class VmManager {
}
}
private final ArrayList<VmInfo> mVmList = new ArrayList<VmInfo>();
private final ArrayList<AvdInfo> mAvdList = new ArrayList<AvdInfo>();
private ISdkLog mSdkLog;
private final SdkManager mSdk;
public VmManager(SdkManager sdk, ISdkLog sdkLog) throws AndroidLocationException {
public AvdManager(SdkManager sdk, ISdkLog sdkLog) throws AndroidLocationException {
mSdk = sdk;
mSdkLog = sdkLog;
buildVmList();
buildAvdList();
}
/**
* Returns the existing VMs.
* @return a newly allocated arrays containing all the VMs.
* Returns the existing AVDs.
* @return a newly allocated array containing all the AVDs.
*/
public VmInfo[] getVms() {
return mVmList.toArray(new VmInfo[mVmList.size()]);
public AvdInfo[] getAvds() {
return mAvdList.toArray(new AvdInfo[mAvdList.size()]);
}
/**
* Returns the {@link VmInfo} matching the given <var>name</var>.
* @return the matching VmInfo or <code>null</code> if none were found.
* Returns the {@link AvdInfo} matching the given <var>name</var>.
* @return the matching AvdInfo or <code>null</code> if none were found.
*/
public VmInfo getVm(String name) {
for (VmInfo info : mVmList) {
public AvdInfo getAvd(String name) {
for (AvdInfo info : mAvdList) {
if (info.name.equals(name)) {
return info;
}
@@ -105,19 +106,20 @@ public final class VmManager {
}
/**
* Creates a new VM. It is expected that there is no existing VM with this name already.
* @param parentFolder the folder to contain the VM. A new folder will be created in this
* folder with the name of the VM
* @param name the name of the VM
* @param target the target of the VM
* Creates a new AVD. It is expected that there is no existing AVD with this name already.
* @param parentFolder the folder to contain the AVD. A new folder will be created in this
* folder with the name of the AVD
* @param name the name of the AVD
* @param target the target of the AVD
* @param skinName the name of the skin. Can be null.
* @param sdcard the parameter value for the sdCard. Can be null. This is either a path to
* an existing sdcard image or a sdcard size (\d+, \d+K, \dM).
* @param hardwareConfig the hardware setup for the VM
* @param hardwareConfig the hardware setup for the AVD
* @param removePrevious If true remove any previous files.
*/
public VmInfo createVm(String parentFolder, String name, IAndroidTarget target,
public AvdInfo createAvd(String parentFolder, String name, IAndroidTarget target,
String skinName, String sdcard, Map<String,String> hardwareConfig,
ISdkLog log) {
boolean removePrevious, ISdkLog log) {
try {
File rootDirectory = new File(parentFolder);
@@ -128,24 +130,31 @@ public final class VmManager {
return null;
}
File vmFolder = new File(parentFolder, name + ".avm");
if (vmFolder.exists()) {
File avdFolder = new File(parentFolder, name + AVD_FOLDER_EXTENSION);
if (avdFolder.exists()) {
if (removePrevious) {
// AVD already exists and removePrevious is set, try to remove the
// directory's content first (but not the directory itself).
recursiveDelete(avdFolder);
} else {
// AVD shouldn't already exist if removePrevious is false.
if (log != null) {
log.error(null, "Folder %s is in the way.", vmFolder.getAbsolutePath());
log.error(null, "Folder %s is in the way.", avdFolder.getAbsolutePath());
}
return null;
}
}
// create the vm folder.
vmFolder.mkdir();
// create the AVD folder.
avdFolder.mkdir();
HashMap<String, String> values = new HashMap<String, String>();
// prepare the ini file.
String vmRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS;
File iniFile = new File(vmRoot, name + ".ini");
values.put(VM_INFO_PATH, vmFolder.getAbsolutePath());
values.put(VM_INFO_TARGET, target.hashString());
String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
File iniFile = new File(avdRoot, name + ".ini");
values.put(AVD_INFO_PATH, avdFolder.getAbsolutePath());
values.put(AVD_INFO_TARGET, target.hashString());
createConfigIni(iniFile, values);
// writes the userdata.img in it.
@@ -153,7 +162,7 @@ public final class VmManager {
File userdataSrc = new File(imagePath, IMAGE_USERDATA);
FileInputStream fis = new FileInputStream(userdataSrc);
File userdataDest = new File(vmFolder, IMAGE_USERDATA);
File userdataDest = new File(avdFolder, IMAGE_USERDATA);
FileOutputStream fos = new FileOutputStream(userdataDest);
byte[] buffer = new byte[4096];
@@ -193,7 +202,7 @@ public final class VmManager {
Matcher m = SDCARD_SIZE_PATTERN.matcher(sdcard);
if (m.matches()) {
// create the sdcard.
sdcardFile = new File(vmFolder, "sdcard.img");
sdcardFile = new File(avdFolder, "sdcard.img");
String path = sdcardFile.getAbsolutePath();
// execute mksdcard with the proper parameters.
@@ -224,28 +233,27 @@ public final class VmManager {
values.putAll(hardwareConfig);
}
File configIniFile = new File(vmFolder, CONFIG_INI);
File configIniFile = new File(avdFolder, CONFIG_INI);
createConfigIni(configIniFile, values);
if (log != null) {
if (target.isPlatform()) {
log.printf("Created VM '%s' based on %s\n", name, target.getName());
log.printf("Created AVD '%s' based on %s\n", name, target.getName());
} else {
log.printf(
"Created VM '%s' based on %s (%s)\n", name, target.getName(),
log.printf("Created AVD '%s' based on %s (%s)\n", name, target.getName(),
target.getVendor());
}
}
// create the VmInfo object, and add it to the list
VmInfo vmInfo = new VmInfo();
vmInfo.name = name;
vmInfo.path = vmFolder.getAbsolutePath();
vmInfo.target = target;
// create the AvdInfo object, and add it to the list
AvdInfo avdInfo = new AvdInfo();
avdInfo.name = name;
avdInfo.path = avdFolder.getAbsolutePath();
avdInfo.target = target;
mVmList.add(vmInfo);
mAvdList.add(avdInfo);
return vmInfo;
return avdInfo;
} catch (AndroidLocationException e) {
if (log != null) {
log.error(e, null);
@@ -259,21 +267,35 @@ public final class VmManager {
return null;
}
private void buildVmList() throws AndroidLocationException {
/**
* Helper method to recursively delete a folder's content (but not the folder itself).
*
* @throws SecurityException like {@link File#delete()} does if file/folder is not writable.
*/
public void recursiveDelete(File folder) {
for (File f : folder.listFiles()) {
if (f.isDirectory()) {
recursiveDelete(folder);
}
f.delete();
}
}
private void buildAvdList() throws AndroidLocationException {
// get the Android prefs location.
String vmRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_VMS;
String avdRoot = AndroidLocation.getFolder() + AndroidLocation.FOLDER_AVD;
// ensure folder validity.
File folder = new File(vmRoot);
File folder = new File(avdRoot);
if (folder.isFile()) {
throw new AndroidLocationException(String.format("%s is not a valid folder.", vmRoot));
throw new AndroidLocationException(String.format("%s is not a valid folder.", avdRoot));
} else if (folder.exists() == false) {
// folder is not there, we create it and return
folder.mkdirs();
return;
}
File[] vms = folder.listFiles(new FilenameFilter() {
File[] avds = folder.listFiles(new FilenameFilter() {
public boolean accept(File parent, String name) {
if (INI_NAME_PATTERN.matcher(name).matches()) {
// check it's a file and not a folder
@@ -284,23 +306,23 @@ public final class VmManager {
}
});
for (File vm : vms) {
VmInfo info = parseVmInfo(vm);
for (File avd : avds) {
AvdInfo info = parseAvdInfo(avd);
if (info != null) {
mVmList.add(info);
mAvdList.add(info);
}
}
}
private VmInfo parseVmInfo(File path) {
private AvdInfo parseAvdInfo(File path) {
Map<String, String> map = SdkManager.parsePropertyFile(path, mSdkLog);
String vmPath = map.get(VM_INFO_PATH);
if (vmPath == null) {
String avdPath = map.get(AVD_INFO_PATH);
if (avdPath == null) {
return null;
}
String targetHash = map.get(VM_INFO_TARGET);
String targetHash = map.get(AVD_INFO_TARGET);
if (targetHash == null) {
return null;
}
@@ -310,14 +332,14 @@ public final class VmManager {
return null;
}
VmInfo info = new VmInfo();
AvdInfo info = new AvdInfo();
Matcher matcher = INI_NAME_PATTERN.matcher(path.getName());
if (matcher.matches()) {
info.name = matcher.group(1);
} else {
info.name = path.getName(); // really this should not happen.
}
info.path = vmPath;
info.path = avdPath;
info.target = target;
return info;
@@ -447,4 +469,14 @@ public final class VmManager {
return process.waitFor();
}
/**
* Removes an {@link AvdInfo} from the internal list.
*
* @param avdInfo The {@link AvdInfo} to remove.
* @return true if this {@link AvdInfo} was present and has been removed.
*/
public boolean removeAvd(AvdInfo avdInfo) {
return mAvdList.remove(avdInfo);
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.sdklib.vm;
package com.android.sdklib.avd;
import com.android.sdklib.ISdkLog;

View File

@@ -17,7 +17,7 @@
package com.android.sdkuilib;
import com.android.sdklib.IAndroidTarget;
import com.android.sdklib.vm.VmManager.VmInfo;
import com.android.sdklib.avd.AvdManager.AvdInfo;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
@@ -40,16 +40,16 @@ import java.util.ArrayList;
/**
* The VM selector is a table that is added to the given parent composite.
* The AVD selector is a table that is added to the given parent composite.
* <p/>
* To use, create it using {@link #VmSelector(Composite, VmInfo[], boolean)} then
* call {@link #setSelection(VmInfo)}, {@link #setSelectionListener(SelectionListener)}
* To use, create it using {@link #AvdSelector(Composite, AvdInfo[], boolean)} then
* call {@link #setSelection(AvdInfo)}, {@link #setSelectionListener(SelectionListener)}
* and finally use {@link #getFirstSelected()} or {@link #getAllSelected()} to retrieve the
* selection.
*/
public final class VmSelector {
public final class AvdSelector {
private VmInfo[] mVms;
private AvdInfo[] mAvds;
private final boolean mAllowMultipleSelection;
private SelectionListener mSelectionListener;
private Table mTable;
@@ -59,12 +59,12 @@ public final class VmSelector {
* Creates a new SDK Target Selector.
*
* @param parent The parent composite where the selector will be added.
* @param vms The list of vms. This is <em>not</em> copied, the caller must not modify.
* @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify.
* @param allowMultipleSelection True if more than one SDK target can be selected at the same
* time.
*/
public VmSelector(Composite parent, VmInfo[] vms, boolean allowMultipleSelection) {
mVms = vms;
public AvdSelector(Composite parent, AvdInfo[] avds, boolean allowMultipleSelection) {
mAvds = avds;
// Layout has 1 column
Composite group = new Composite(parent, SWT.NONE);
@@ -89,7 +89,7 @@ public final class VmSelector {
// create the table columns
final TableColumn column0 = new TableColumn(mTable, SWT.NONE);
column0.setText("VM Name");
column0.setText("AVD Name");
final TableColumn column1 = new TableColumn(mTable, SWT.NONE);
column1.setText("Target Name");
final TableColumn column2 = new TableColumn(mTable, SWT.NONE);
@@ -104,25 +104,25 @@ public final class VmSelector {
}
/**
* Sets a new set of VM, with an optional filter.
* Sets a new set of AVD, with an optional filter.
* <p/>This must be called from the UI thread.
*
* @param vms The list of vms. This is <em>not</em> copied, the caller must not modify.
* @param filter An IAndroidTarget. If non-null, only VM whose target are compatible with the
* @param avds The list of AVDs. This is <em>not</em> copied, the caller must not modify.
* @param filter An IAndroidTarget. If non-null, only AVD whose target are compatible with the
* filter target will displayed an available for selection.
*/
public void setVms(VmInfo[] vms, IAndroidTarget filter) {
mVms = vms;
public void setAvds(AvdInfo[] avds, IAndroidTarget filter) {
mAvds = avds;
fillTable(mTable, filter);
}
/**
* Returns the list of known Vms.
* Returns the list of known AVDs.
* <p/>
* This is not a copy. Callers must <em>not</em> modify this array.
*/
public VmInfo[] getVms() {
return mVms;
public AvdInfo[] getAvds() {
return mAvds;
}
/**
@@ -151,11 +151,11 @@ public final class VmSelector {
* @param target the target to be selection
* @return true if the target could be selected, false otherwise.
*/
public boolean setSelection(VmInfo target) {
public boolean setSelection(AvdInfo target) {
boolean found = false;
boolean modified = false;
for (TableItem i : mTable.getItems()) {
if ((VmInfo) i.getData() == target) {
if ((AvdInfo) i.getData() == target) {
found = true;
if (!i.getChecked()) {
modified = true;
@@ -181,14 +181,14 @@ public final class VmSelector {
* @see #getFirstSelected()
* @return An array of selected items. The list can be empty but not null.
*/
public VmInfo[] getAllSelected() {
public AvdInfo[] getAllSelected() {
ArrayList<IAndroidTarget> list = new ArrayList<IAndroidTarget>();
for (TableItem i : mTable.getItems()) {
if (i.getChecked()) {
list.add((IAndroidTarget) i.getData());
}
}
return list.toArray(new VmInfo[list.size()]);
return list.toArray(new AvdInfo[list.size()]);
}
/**
@@ -198,10 +198,10 @@ public final class VmSelector {
* @see #getAllSelected()
* @return The first selected item or null.
*/
public VmInfo getFirstSelected() {
public AvdInfo getFirstSelected() {
for (TableItem i : mTable.getItems()) {
if (i.getChecked()) {
return (VmInfo) i.getData();
return (AvdInfo) i.getData();
}
}
return null;
@@ -283,7 +283,7 @@ public final class VmSelector {
}
/**
* Fills the table with all VM.
* Fills the table with all AVD.
* The table columns are:
* <ul>
* <li>column 0: sdk name
@@ -294,14 +294,14 @@ public final class VmSelector {
*/
private void fillTable(final Table table, IAndroidTarget filter) {
table.removeAll();
if (mVms != null && mVms.length > 0) {
if (mAvds != null && mAvds.length > 0) {
table.setEnabled(true);
for (VmInfo vm : mVms) {
if (filter == null || filter.isCompatibleBaseFor(vm.getTarget())) {
for (AvdInfo avd : mAvds) {
if (filter == null || filter.isCompatibleBaseFor(avd.getTarget())) {
TableItem item = new TableItem(table, SWT.NONE);
item.setData(vm);
item.setText(0, vm.getName());
IAndroidTarget target = vm.getTarget();
item.setData(avd);
item.setText(0, avd.getName());
IAndroidTarget target = avd.getTarget();
item.setText(1, target.getFullName());
item.setText(2, target.getApiVersionName());
item.setText(3, Integer.toString(target.getApiVersionNumber()));
@@ -314,7 +314,7 @@ public final class VmSelector {
TableItem item = new TableItem(table, SWT.NONE);
item.setData(null);
item.setText(0, "--");
item.setText(1, "No VM available");
item.setText(1, "No AVD available");
item.setText(2, "--");
item.setText(3, "--");
}
@@ -365,13 +365,13 @@ public final class VmSelector {
}
/**
* Updates the description label with the path of the item's VM, if any.
* Updates the description label with the path of the item's AVD, if any.
*/
private void updateDescription(TableItem item) {
if (item != null) {
Object data = item.getData();
if (data instanceof VmInfo) {
String newTooltip = ((VmInfo) data).getPath();
if (data instanceof AvdInfo) {
String newTooltip = ((AvdInfo) data).getPath();
mDescription.setText(newTooltip == null ? "" : newTooltip); //$NON-NLS-1$
}
}