Merge "Multiple asset adding in one shot for AssetManager"
diff --git a/api/current.xml b/api/current.xml
index 689f5dd..d329f3d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -199211,6 +199211,223 @@
</parameter>
</method>
</interface>
+<class name="NumberPicker"
+ extends="android.widget.LinearLayout"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="NumberPicker"
+ type="android.widget.NumberPicker"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<constructor name="NumberPicker"
+ type="android.widget.NumberPicker"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="attrs" type="android.util.AttributeSet">
+</parameter>
+</constructor>
+<method name="changeCurrent"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="current" type="int">
+</parameter>
+</method>
+<method name="getBeginRange"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
+<method name="getCurrent"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getEndRange"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
+<method name="setCurrent"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="current" type="int">
+</parameter>
+</method>
+<method name="setFormatter"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="formatter" type="android.widget.NumberPicker.Formatter">
+</parameter>
+</method>
+<method name="setOnChangeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.widget.NumberPicker.OnChangedListener">
+</parameter>
+</method>
+<method name="setRange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+</method>
+<method name="setRange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+<parameter name="displayedValues" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="setSpeed"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="speed" type="long">
+</parameter>
+</method>
+<field name="TWO_DIGIT_FORMATTER"
+ type="android.widget.NumberPicker.Formatter"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="NumberPicker.Formatter"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="toString"
+ return="java.lang.String"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="int">
+</parameter>
+</method>
+</interface>
+<interface name="NumberPicker.OnChangedListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="picker" type="android.widget.NumberPicker">
+</parameter>
+<parameter name="oldVal" type="int">
+</parameter>
+<parameter name="newVal" type="int">
+</parameter>
+</method>
+</interface>
<class name="PopupWindow"
extends="java.lang.Object"
abstract="false"
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index fae1f26..a8e217e 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -235,6 +235,7 @@
// Callback is disabled by default
mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
+ mOrientation = 0;
cameraService->incUsers();
LOGV("Client::Client X (pid %d)", callingPid);
}
@@ -570,7 +571,8 @@
// wait in the createOverlay call if the previous overlay is in the
// process of being destroyed.
for (int retry = 0; retry < 50; ++retry) {
- mOverlayRef = mSurface->createOverlay(w, h, OVERLAY_FORMAT_DEFAULT);
+ mOverlayRef = mSurface->createOverlay(w, h, OVERLAY_FORMAT_DEFAULT,
+ mOrientation);
if (mOverlayRef != NULL) break;
LOGW("Overlay create failed - retrying");
usleep(20000);
@@ -601,15 +603,9 @@
CameraParameters params(mHardware->getParameters());
params.getPreviewSize(&w, &h);
- uint32_t transform = 0;
- if (params.getOrientation() ==
- CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
- LOGV("portrait mode");
- transform = ISurface::BufferHeap::ROT_90;
- }
ISurface::BufferHeap buffers(w, h, w, h,
PIXEL_FORMAT_YCbCr_420_SP,
- transform,
+ mOrientation,
0,
mHardware->getPreviewHeap());
@@ -919,12 +915,6 @@
if (mSurface != 0 && !mUseOverlay) {
int w, h;
CameraParameters params(mHardware->getParameters());
- uint32_t transform = 0;
- if (params.getOrientation() == CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
- LOGV("portrait mode");
- transform = ISurface::BufferHeap::ROT_90;
- }
-
if (size == NULL) {
params.getPictureSize(&w, &h);
} else {
@@ -935,7 +925,7 @@
LOGV("Snapshot image width=%d, height=%d", w, h);
}
ISurface::BufferHeap buffers(w, h, w, h,
- PIXEL_FORMAT_YCbCr_420_SP, transform, 0, mHardware->getRawHeap());
+ PIXEL_FORMAT_YCbCr_420_SP, mOrientation, 0, mHardware->getRawHeap());
mSurface->registerBuffers(buffers);
}
@@ -1200,6 +1190,15 @@
}
CameraParameters p(params);
+
+ // The orientation parameter is actually for CameraService, not for the camera driver.
+ if (p.getOrientation() == CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
+ LOGV("portrait mode");
+ mOrientation = ISurface::BufferHeap::ROT_90;
+ } else {
+ mOrientation = 0;
+ }
+
return mHardware->setParameters(p);
}
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index 3e3e54f..b3d20f6 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -182,6 +182,7 @@
sp<CameraService> mCameraService;
sp<ISurface> mSurface;
int mPreviewCallbackFlag;
+ int mOrientation;
sp<MediaPlayer> mMediaPlayerClick;
sp<MediaPlayer> mMediaPlayerBeep;
diff --git a/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/camera/tests/CameraServiceTest/CameraServiceTest.cpp
index 29320e0..f89d9d3 100644
--- a/camera/tests/CameraServiceTest/CameraServiceTest.cpp
+++ b/camera/tests/CameraServiceTest/CameraServiceTest.cpp
@@ -283,7 +283,7 @@
virtual void postBuffer(ssize_t offset);
virtual void unregisterBuffers();
virtual sp<OverlayRef> createOverlay(
- uint32_t w, uint32_t h, int32_t format);
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation);
virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage);
// new functions
@@ -346,7 +346,8 @@
}
}
-sp<OverlayRef> MSurface::createOverlay(uint32_t w, uint32_t h, int32_t format) {
+sp<OverlayRef> MSurface::createOverlay(uint32_t w, uint32_t h, int32_t format,
+ int32_t orientation) {
// We don't expect this to be called in current hardware.
ASSERT(0);
sp<OverlayRef> dummy;
diff --git a/core/java/android/app/DeviceAdmin.java b/core/java/android/app/DeviceAdmin.java
index 4da3fee..16832db 100644
--- a/core/java/android/app/DeviceAdmin.java
+++ b/core/java/android/app/DeviceAdmin.java
@@ -77,10 +77,10 @@
/**
* Action sent to a device administrator when the user has changed the
* password of their device. You can at this point check the characteristics
- * of the new password with {@link DevicePolicyManager#getActivePasswordMode()
+ * of the new password with {@link DevicePolicyManager#getPasswordMode()
* DevicePolicyManager.getActivePasswordMode()} and
- * {@link DevicePolicyManager#getActiveMinimumPasswordLength()
- * DevicePolicyManager.getActiveMinimumPasswordLength()}. You will generally
+ * {@link DevicePolicyManager#getMinimumPasswordLength()
+ * DevicePolicyManager.getMinimumPasswordLength()}. You will generally
* handle this in {@link DeviceAdmin#onPasswordChanged(Context, Intent)}.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index 0603ca5..714a611 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -56,7 +56,7 @@
if (Config.LOGV)
Log.v("ddm-hello", "Connected!");
- if (true) {
+ if (false) {
/* test spontaneous transmission */
byte[] data = new byte[] { 0, 1, 2, 3, 4, -4, -3, -2, -1, 127 };
Chunk testChunk =
@@ -148,9 +148,7 @@
private Chunk handleFEAT(Chunk request) {
// TODO: query the VM to ensure that support for these features
// is actually compiled in
- final String[] features = {
- "hprof-heap-dump", "method-trace-profiling"
- };
+ final String[] features = Debug.getVmFeatureList();
if (Config.LOGV)
Log.v("ddm-heap", "Got feature list request");
diff --git a/core/java/android/net/InterfaceConfiguration.aidl b/core/java/android/net/InterfaceConfiguration.aidl
new file mode 100644
index 0000000..8aa5e34
--- /dev/null
+++ b/core/java/android/net/InterfaceConfiguration.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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 android.net;
+
+parcelable InterfaceConfiguration;
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
new file mode 100644
index 0000000..9aa23fe
--- /dev/null
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -0,0 +1,83 @@
+/*
+ * 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 android.net;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * A simple object for retrieving / setting an interfaces configuration
+ * @hide
+ */
+public class InterfaceConfiguration implements Parcelable {
+ public String hwAddr;
+ public int ipAddr;
+ public int netmask;
+ public String interfaceFlags;
+
+ public InterfaceConfiguration() {
+ super();
+ }
+
+ public String toString() {
+ StringBuffer str = new StringBuffer();
+
+ str.append("ipddress "); putAddress(str, ipAddr);
+ str.append(" netmask "); putAddress(str, netmask);
+ str.append(" flags ").append(interfaceFlags);
+ str.append(" hwaddr ").append(hwAddr);
+
+ return str.toString();
+ }
+
+ private static void putAddress(StringBuffer buf, int addr) {
+ buf.append(addr & 0xff).append('.').
+ append((addr >>>= 8) & 0xff).append('.').
+ append((addr >>>= 8) & 0xff).append('.').
+ append((addr >>>= 8) & 0xff);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(hwAddr);
+ dest.writeInt(ipAddr);
+ dest.writeInt(netmask);
+ dest.writeString(interfaceFlags);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<InterfaceConfiguration> CREATOR =
+ new Creator<InterfaceConfiguration>() {
+ public InterfaceConfiguration createFromParcel(Parcel in) {
+ InterfaceConfiguration info = new InterfaceConfiguration();
+ info.hwAddr = in.readString();
+ info.ipAddr = in.readInt();
+ info.netmask = in.readInt();
+ info.interfaceFlags = in.readString();
+ return info;
+ }
+
+ public InterfaceConfiguration[] newArray(int size) {
+ return new InterfaceConfiguration[size];
+ }
+ };
+}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index b33e8be..7043c2e 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -264,6 +264,17 @@
}
/**
+ * Returns an array of strings that identify VM features. This is
+ * used by DDMS to determine what sorts of operations the VM can
+ * perform.
+ *
+ * @hide
+ */
+ public static String[] getVmFeatureList() {
+ return VMDebug.getVmFeatureList();
+ }
+
+ /**
* Change the JDWP port.
*
* @deprecated no longer needed or useful
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index fe416c5..49abb4d 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -17,6 +17,8 @@
package android.os;
+import android.net.InterfaceConfiguration;
+
/**
* @hide
*/
@@ -32,6 +34,17 @@
String[] listInterfaces();
/**
+ * Retrieves the specified interface config
+ *
+ */
+ InterfaceConfiguration getInterfaceConfig(String iface);
+
+ /**
+ * Sets the configuration of the specified interface
+ */
+ void setInterfaceConfig(String iface, in InterfaceConfiguration cfg);
+
+ /**
* Shuts down the service
*/
void shutdown();
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 509aac5..3296bbf 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -229,7 +229,7 @@
* The content:// style URL for this table
*/
public static final Uri CONTENT_URI =
- Uri.parse("content://calendar/calendars");
+ Uri.parse("content://" + AUTHORITY + "/calendars");
/**
* The default sort order for this table
@@ -332,7 +332,7 @@
public static final class Attendees implements BaseColumns,
AttendeesColumns, EventsColumns {
public static final Uri CONTENT_URI =
- Uri.parse("content://calendar/attendees");
+ Uri.parse("content://" + AUTHORITY + "/attendees");
// TODO: fill out this class when we actually start utilizing attendees
// in the calendar application.
@@ -576,7 +576,8 @@
/**
* The content:// style URL for this table
*/
- public static final Uri CONTENT_URI = Uri.parse("content://calendar/event_entities");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
+ "/event_entities");
/**
* The name of the account instance to which this row belongs, which when paired with
@@ -914,10 +915,10 @@
* The content:// style URL for this table
*/
public static final Uri CONTENT_URI =
- Uri.parse("content://calendar/events");
+ Uri.parse("content://" + AUTHORITY + "/events");
public static final Uri DELETED_CONTENT_URI =
- Uri.parse("content://calendar/deleted_events");
+ Uri.parse("content://" + AUTHORITY + "/deleted_events");
/**
* The default sort order for this table
@@ -957,9 +958,10 @@
/**
* The content:// style URL for this table
*/
- public static final Uri CONTENT_URI = Uri.parse("content://calendar/instances/when");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
+ "/instances/when");
public static final Uri CONTENT_BY_DAY_URI =
- Uri.parse("content://calendar/instances/whenbyday");
+ Uri.parse("content://" + AUTHORITY + "/instances/whenbyday");
/**
* The default sort order for this table.
@@ -1075,7 +1077,8 @@
}
public static final class EventDays implements EventDaysColumns {
- public static final Uri CONTENT_URI = Uri.parse("content://calendar/instances/groupbyday");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
+ "/instances/groupbyday");
public static final String[] PROJECTION = { STARTDAY, ENDDAY };
public static final String SELECTION = "selected==1";
@@ -1134,7 +1137,7 @@
public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns {
public static final String TABLE_NAME = "Reminders";
- public static final Uri CONTENT_URI = Uri.parse("content://calendar/reminders");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders");
}
public interface CalendarAlertsColumns {
@@ -1210,7 +1213,8 @@
public static final class CalendarAlerts implements BaseColumns,
CalendarAlertsColumns, EventsColumns, CalendarsColumns {
public static final String TABLE_NAME = "CalendarAlerts";
- public static final Uri CONTENT_URI = Uri.parse("content://calendar/calendar_alerts");
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
+ "/calendar_alerts");
/**
* This URI is for grouping the query results by event_id and begin
@@ -1219,7 +1223,7 @@
* instances of a repeating event will show up multiple times.
*/
public static final Uri CONTENT_URI_BY_INSTANCE =
- Uri.parse("content://calendar/calendar_alerts/by_instance");
+ Uri.parse("content://" + AUTHORITY + "/calendar_alerts/by_instance");
private static final boolean DEBUG = true;
@@ -1404,7 +1408,7 @@
public static final class ExtendedProperties implements BaseColumns,
ExtendedPropertiesColumns, EventsColumns {
public static final Uri CONTENT_URI =
- Uri.parse("content://calendar/extendedproperties");
+ Uri.parse("content://" + AUTHORITY + "/extendedproperties");
// TODO: fill out this class when we actually start utilizing extendedproperties
// in the calendar application.
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 2da8f14..50bfefc 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -17,6 +17,8 @@
package android.view;
import android.content.Context;
+import android.util.DisplayMetrics;
+import android.util.Log;
/**
* Detects transformation gestures involving more than one pointer ("multitouch")
@@ -137,10 +139,20 @@
private float mCurrPressure;
private float mPrevPressure;
private long mTimeDelta;
+
+ private float mEdgeSlop;
+ private float mRightSlopEdge;
+ private float mBottomSlopEdge;
+ private boolean mSloppyGesture;
public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
+ ViewConfiguration config = ViewConfiguration.get(context);
+ DisplayMetrics metrics = context.getResources().getDisplayMetrics();
mContext = context;
mListener = listener;
+ mEdgeSlop = config.getScaledEdgeSlop();
+ mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
+ mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
}
public boolean onTouchEvent(MotionEvent event) {
@@ -152,15 +164,68 @@
action == MotionEvent.ACTION_POINTER_2_DOWN) &&
event.getPointerCount() >= 2) {
// We have a new multi-finger gesture
-
+
// Be paranoid in case we missed an event
reset();
-
+
mPrevEvent = MotionEvent.obtain(event);
mTimeDelta = 0;
-
+
setContext(event);
- mGestureInProgress = mListener.onScaleBegin(this);
+
+ // Check if we have a sloppy gesture. If so, delay
+ // the beginning of the gesture until we're sure that's
+ // what the user wanted. Sloppy gestures can happen if the
+ // edge of the user's hand is touching the screen, for example.
+ final float edgeSlop = mEdgeSlop;
+ final float rightSlop = mRightSlopEdge;
+ final float bottomSlop = mBottomSlopEdge;
+ final float x0 = event.getRawX();
+ final float y0 = event.getRawY();
+ final float x1 = getRawX(event, 1);
+ final float y1 = getRawY(event, 1);
+
+ boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop ||
+ x1 < edgeSlop || y1 < edgeSlop;
+ boolean p1sloppy = x0 > rightSlop || y0 > bottomSlop ||
+ x1 > rightSlop || y1 > bottomSlop;
+
+ if (p0sloppy) {
+ mFocusX = event.getX(1);
+ mFocusY = event.getY(1);
+ mSloppyGesture = true;
+ } else if (p1sloppy) {
+ mFocusX = event.getX(0);
+ mFocusY = event.getY(0);
+ mSloppyGesture = true;
+ } else {
+ mGestureInProgress = mListener.onScaleBegin(this);
+ }
+ } else if (action == MotionEvent.ACTION_MOVE && mSloppyGesture) {
+ // Initiate sloppy gestures if we've moved outside of the slop area.
+ final float edgeSlop = mEdgeSlop;
+ final float rightSlop = mRightSlopEdge;
+ final float bottomSlop = mBottomSlopEdge;
+ final float x0 = event.getRawX();
+ final float y0 = event.getRawY();
+ final float x1 = getRawX(event, 1);
+ final float y1 = getRawY(event, 1);
+
+ boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop ||
+ x1 < edgeSlop || y1 < edgeSlop;
+ boolean p1sloppy = x0 > rightSlop || y0 > bottomSlop ||
+ x1 > rightSlop || y1 > bottomSlop;
+
+ if (p0sloppy) {
+ mFocusX = event.getX(1);
+ mFocusY = event.getY(1);
+ } else if (p1sloppy) {
+ mFocusX = event.getX(0);
+ mFocusY = event.getY(0);
+ } else {
+ mSloppyGesture = false;
+ mGestureInProgress = mListener.onScaleBegin(this);
+ }
}
} else {
// Transform gesture in progress - attempt to handle it
@@ -169,22 +234,24 @@
case MotionEvent.ACTION_POINTER_2_UP:
// Gesture ended
setContext(event);
-
+
// Set focus point to the remaining finger
int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
>> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
mFocusX = event.getX(id);
mFocusY = event.getY(id);
-
- mListener.onScaleEnd(this);
- mGestureInProgress = false;
+
+ if (!mSloppyGesture) {
+ mListener.onScaleEnd(this);
+ }
reset();
break;
case MotionEvent.ACTION_CANCEL:
- mListener.onScaleEnd(this);
- mGestureInProgress = false;
+ if (!mSloppyGesture) {
+ mListener.onScaleEnd(this);
+ }
reset();
break;
@@ -208,6 +275,22 @@
}
return handled;
}
+
+ /**
+ * MotionEvent has no getRawX(int) method; simulate it pending future API approval.
+ */
+ private static float getRawX(MotionEvent event, int pointerIndex) {
+ float offset = event.getX() - event.getRawX();
+ return event.getX(pointerIndex) + offset;
+ }
+
+ /**
+ * MotionEvent has no getRawY(int) method; simulate it pending future API approval.
+ */
+ private static float getRawY(MotionEvent event, int pointerIndex) {
+ float offset = event.getY() - event.getRawY();
+ return event.getY(pointerIndex) + offset;
+ }
private void setContext(MotionEvent curr) {
if (mCurrEvent != null) {
@@ -255,6 +338,8 @@
mCurrEvent.recycle();
mCurrEvent = null;
}
+ mSloppyGesture = false;
+ mGestureInProgress = false;
}
/**
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 3ebe1c2..616485e 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -90,6 +90,15 @@
public static final boolean TRACE_RECYCLER = false;
/**
+ * Enables or disables motion events tracing. Any invoker of
+ * {@link #trace(View, MotionEvent, MotionEventTraceType)} should first check
+ * that this value is set to true as not to affect performance.
+ *
+ * @hide
+ */
+ public static final boolean TRACE_MOTION_EVENTS = false;
+
+ /**
* The system property of dynamic switch for capturing view information
* when it is set, we dump interested fields and methods for the view on focus
*/
@@ -367,7 +376,6 @@
BIND_VIEW,
RECYCLE_FROM_ACTIVE_HEAP,
RECYCLE_FROM_SCRAP_HEAP,
- MOVE_TO_ACTIVE_HEAP,
MOVE_TO_SCRAP_HEAP,
MOVE_FROM_ACTIVE_TO_SCRAP_HEAP
}
@@ -385,6 +393,21 @@
private static String sRecyclerTracePrefix;
/**
+ * Defines the type of motion events trace to output to the motion events traces file.
+ *
+ * @hide
+ */
+ public enum MotionEventTraceType {
+ DISPATCH,
+ ON_INTERCEPT,
+ ON_TOUCH
+ }
+
+ private static BufferedWriter sMotionEventTraces;
+ private static ViewRoot sMotionEventRoot;
+ private static String sMotionEventTracePrefix;
+
+ /**
* Returns the number of instanciated Views.
*
* @return The number of Views instanciated in the current process.
@@ -675,6 +698,146 @@
sHierarhcyRoot = null;
}
+ /**
+ * Outputs a trace to the currently opened traces file. The trace contains the class name
+ * and instance's hashcode of the specified view as well as the supplied trace type.
+ *
+ * @param view the view to trace
+ * @param event the event of the trace
+ * @param type the type of the trace
+ *
+ * @hide
+ */
+ public static void trace(View view, MotionEvent event, MotionEventTraceType type) {
+ if (sMotionEventTraces == null) {
+ return;
+ }
+
+ try {
+ sMotionEventTraces.write(type.name());
+ sMotionEventTraces.write(' ');
+ sMotionEventTraces.write(event.getAction());
+ sMotionEventTraces.write(' ');
+ sMotionEventTraces.write(view.getClass().getName());
+ sMotionEventTraces.write('@');
+ sMotionEventTraces.write(Integer.toHexString(view.hashCode()));
+ sHierarchyTraces.newLine();
+ } catch (IOException e) {
+ Log.w("View", "Error while dumping trace of event " + event + " for view " + view);
+ }
+ }
+
+ /**
+ * Starts tracing the motion events for the hierarchy of the specificy view.
+ * The trace is identified by a prefix, used to build the traces files names:
+ * <code>/EXTERNAL/motion-events/PREFIX.traces</code> and
+ * <code>/EXTERNAL/motion-events/PREFIX.tree</code>.
+ *
+ * Only one view hierarchy can be traced at the same time. After calling this method, any
+ * other invocation will result in a <code>IllegalStateException</code> unless
+ * {@link #stopMotionEventTracing()} is invoked before.
+ *
+ * Calling this method creates the file <code>/EXTERNAL/motion-events/PREFIX.traces</code>
+ * containing all the traces (or method calls) relative to the specified view's hierarchy.
+ *
+ * This method will return immediately if TRACE_HIERARCHY is false.
+ *
+ * @param prefix the traces files name prefix
+ * @param view the view whose hierarchy must be traced
+ *
+ * @see #stopMotionEventTracing()
+ * @see #trace(View, MotionEvent, android.view.ViewDebug.MotionEventTraceType)
+ *
+ * @hide
+ */
+ public static void startMotionEventTracing(String prefix, View view) {
+ //noinspection PointlessBooleanExpression,ConstantConditions
+ if (!TRACE_MOTION_EVENTS) {
+ return;
+ }
+
+ if (sMotionEventRoot != null) {
+ throw new IllegalStateException("You must call stopMotionEventTracing() before running" +
+ " a new trace!");
+ }
+
+ File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "motion-events/");
+ //noinspection ResultOfMethodCallIgnored
+ hierarchyDump.mkdirs();
+
+ hierarchyDump = new File(hierarchyDump, prefix + ".traces");
+ sMotionEventTracePrefix = prefix;
+
+ try {
+ sMotionEventTraces = new BufferedWriter(new FileWriter(hierarchyDump), 32 * 1024);
+ } catch (IOException e) {
+ Log.e("View", "Could not dump view hierarchy");
+ return;
+ }
+
+ sMotionEventRoot = (ViewRoot) view.getRootView().getParent();
+ }
+
+ /**
+ * Stops the current motion events tracing. This method closes the file
+ * <code>/EXTERNAL/motion-events/PREFIX.traces</code>.
+ *
+ * Calling this method creates the file <code>/EXTERNAL/motion-events/PREFIX.tree</code>
+ * containing the view hierarchy of the view supplied to
+ * {@link #startMotionEventTracing(String, View)}.
+ *
+ * This method will return immediately if TRACE_HIERARCHY is false.
+ *
+ * @see #startMotionEventTracing(String, View)
+ * @see #trace(View, MotionEvent, android.view.ViewDebug.MotionEventTraceType)
+ *
+ * @hide
+ */
+ public static void stopMotionEventTracing() {
+ //noinspection PointlessBooleanExpression,ConstantConditions
+ if (!TRACE_MOTION_EVENTS) {
+ return;
+ }
+
+ if (sMotionEventRoot == null || sMotionEventTraces == null) {
+ throw new IllegalStateException("You must call startMotionEventTracing() before" +
+ " stopMotionEventTracing()!");
+ }
+
+ try {
+ sMotionEventTraces.close();
+ } catch (IOException e) {
+ Log.e("View", "Could not write view traces");
+ }
+ sMotionEventTraces = null;
+
+ File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "motion-events/");
+ //noinspection ResultOfMethodCallIgnored
+ hierarchyDump.mkdirs();
+ hierarchyDump = new File(hierarchyDump, sMotionEventTracePrefix + ".tree");
+
+ BufferedWriter out;
+ try {
+ out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
+ } catch (IOException e) {
+ Log.e("View", "Could not dump view hierarchy");
+ return;
+ }
+
+ View view = sMotionEventRoot.getView();
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+ dumpViewHierarchy(group, out, 0);
+ try {
+ out.close();
+ } catch (IOException e) {
+ Log.e("View", "Could not dump view hierarchy");
+ }
+ }
+
+ sHierarhcyRoot = null;
+ }
+
static void dispatchCommand(View view, String command, String parameters,
OutputStream clientStream) throws IOException {
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index b657e8e..299ed8a 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -25,9 +25,9 @@
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.LayoutInflater;
+import android.widget.NumberPicker;
+import android.widget.NumberPicker.OnChangedListener;
-import com.android.common.widget.NumberPicker;
-import com.android.common.widget.NumberPicker.OnChangedListener;
import com.android.internal.R;
import java.text.DateFormatSymbols;
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 0ce70fa..52a8679 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -94,6 +94,9 @@
@ViewDebug.ExportedProperty
private float mWeightSum;
+ @ViewDebug.ExportedProperty
+ private boolean mUseLargestChild;
+
private int[] mMaxAscent;
private int[] mMaxDescent;
@@ -102,7 +105,7 @@
private static final int INDEX_CENTER_VERTICAL = 0;
private static final int INDEX_TOP = 1;
private static final int INDEX_BOTTOM = 2;
- private static final int INDEX_FILL = 3;
+ private static final int INDEX_FILL = 3;
public LinearLayout(Context context) {
super(context);
@@ -134,6 +137,9 @@
mBaselineAlignedChildIndex =
a.getInt(com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1);
+ // TODO: Better name, add Java APIs, make it public
+ mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_useLargestChild, false);
+
a.recycle();
}
@@ -328,6 +334,9 @@
boolean matchWidth = false;
final int baselineChildIndex = mBaselineAlignedChildIndex;
+ final boolean useLargestChild = mUseLargestChild;
+
+ int largestChildHeight = Integer.MIN_VALUE;
// See how tall everyone is. Also remember max width.
for (int i = 0; i < count; ++i) {
@@ -353,30 +362,35 @@
// there is any leftover space.
mTotalLength += lp.topMargin + lp.bottomMargin;
} else {
- int oldHeight = Integer.MIN_VALUE;
+ int oldHeight = Integer.MIN_VALUE;
- if (lp.height == 0 && lp.weight > 0) {
+ if (lp.height == 0 && lp.weight > 0) {
// heightMode is either UNSPECIFIED OR AT_MOST, and this child
// wanted to stretch to fill available space. Translate that to
// WRAP_CONTENT so that it does not end up with a height of 0
oldHeight = 0;
lp.height = LayoutParams.WRAP_CONTENT;
- }
+ }
- // Determine how big this child would like to. If this or
- // previous children have given a weight, then we allow it to
- // use all available space (and we will shrink things later
- // if needed).
- measureChildBeforeLayout(
+ // Determine how big this child would like to. If this or
+ // previous children have given a weight, then we allow it to
+ // use all available space (and we will shrink things later
+ // if needed).
+ measureChildBeforeLayout(
child, i, widthMeasureSpec, 0, heightMeasureSpec,
totalWeight == 0 ? mTotalLength : 0);
- if (oldHeight != Integer.MIN_VALUE) {
+ if (oldHeight != Integer.MIN_VALUE) {
lp.height = oldHeight;
- }
+ }
- mTotalLength += child.getMeasuredHeight() + lp.topMargin +
+ final int childHeight = child.getMeasuredHeight();
+ mTotalLength += childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child);
+
+ if (useLargestChild) {
+ largestChildHeight = Math.max(childHeight, largestChildHeight);
+ }
}
/**
@@ -426,7 +440,30 @@
i += getChildrenSkipCount(child, i);
}
-
+
+ if (useLargestChild) {
+ mTotalLength = 0;
+
+ for (int i = 0; i < count; ++i) {
+ final View child = getVirtualChildAt(i);
+
+ if (child == null) {
+ mTotalLength += measureNullChild(i);
+ continue;
+ }
+
+ if (child.getVisibility() == GONE) {
+ i += getChildrenSkipCount(child, i);
+ continue;
+ }
+
+ final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
+ child.getLayoutParams();
+ mTotalLength += largestChildHeight + lp.topMargin+ lp.bottomMargin +
+ getNextLocationOffset(child);
+ }
+ }
+
// Add in our padding
mTotalLength += mPaddingTop + mPaddingBottom;
@@ -587,6 +624,9 @@
maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1;
final boolean baselineAligned = mBaselineAligned;
+ final boolean useLargestChild = mUseLargestChild;
+
+ int largestChildWidth = Integer.MIN_VALUE;
// See how wide everyone is. Also remember max height.
for (int i = 0; i < count; ++i) {
@@ -602,7 +642,8 @@
continue;
}
- final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
+ final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
+ child.getLayoutParams();
totalWeight += lp.weight;
@@ -643,9 +684,14 @@
if (oldWidth != Integer.MIN_VALUE) {
lp.width = oldWidth;
}
-
- mTotalLength += child.getMeasuredWidth() + lp.leftMargin +
- lp.rightMargin + getNextLocationOffset(child);
+
+ final int childWidth = child.getMeasuredWidth();
+ mTotalLength += childWidth + lp.leftMargin + lp.rightMargin +
+ getNextLocationOffset(child);
+
+ if (useLargestChild) {
+ largestChildWidth = Math.max(childWidth, largestChildWidth);
+ }
}
boolean matchHeightLocally = false;
@@ -708,6 +754,29 @@
maxHeight = Math.max(maxHeight, ascent + descent);
}
+ if (useLargestChild && widthMode == MeasureSpec.AT_MOST) {
+ mTotalLength = 0;
+
+ for (int i = 0; i < count; ++i) {
+ final View child = getVirtualChildAt(i);
+
+ if (child == null) {
+ mTotalLength += measureNullChild(i);
+ continue;
+ }
+
+ if (child.getVisibility() == GONE) {
+ i += getChildrenSkipCount(child, i);
+ continue;
+ }
+
+ final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
+ child.getLayoutParams();
+ mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
+ getNextLocationOffset(child);
+ }
+ }
+
// Add in our padding
mTotalLength += mPaddingLeft + mPaddingRight;
@@ -953,7 +1022,7 @@
final int paddingLeft = mPaddingLeft;
int childTop = mPaddingTop;
- int childLeft = paddingLeft;
+ int childLeft;
// Where right end of child should go
final int width = mRight - mLeft;
@@ -1038,7 +1107,7 @@
void layoutHorizontal() {
final int paddingTop = mPaddingTop;
- int childTop = paddingTop;
+ int childTop;
int childLeft = mPaddingLeft;
// Where bottom of child should go
@@ -1103,7 +1172,7 @@
break;
case Gravity.CENTER_VERTICAL:
- // Removed support for baselign alignment when layout_gravity or
+ // Removed support for baseline alignment when layout_gravity or
// gravity == center_vertical. See bug #1038483.
// Keep the code around if we need to re-enable this feature
// if (childBaseline != -1) {
diff --git a/common/java/com/android/common/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
similarity index 65%
rename from common/java/com/android/common/widget/NumberPicker.java
rename to core/java/android/widget/NumberPicker.java
index 64b436f..2d36bc8 100644
--- a/common/java/com/android/common/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-package com.android.common.widget;
+package android.widget;
+import android.annotation.Widget;
import android.content.Context;
import android.os.Handler;
import android.text.InputFilter;
@@ -34,13 +35,29 @@
import com.android.internal.R;
-public class NumberPicker extends LinearLayout implements OnClickListener,
- OnFocusChangeListener, OnLongClickListener {
+/**
+ * A view for selecting a number
+ *
+ * For a dialog using this view, see {@link android.app.TimePickerDialog}.
+ */
+@Widget
+public class NumberPicker extends LinearLayout {
+ /**
+ * The callback interface used to indicate the number value has been adjusted.
+ */
public interface OnChangedListener {
+ /**
+ * @param picker The NumberPicker associated with this listener.
+ * @param oldVal The previous value.
+ * @param newVal The new value.
+ */
void onChanged(NumberPicker picker, int oldVal, int newVal);
}
+ /**
+ * Interface used to format the number into a string for presentation
+ */
public interface Formatter {
String toString(int value);
}
@@ -81,10 +98,26 @@
private final InputFilter mNumberInputFilter;
private String[] mDisplayedValues;
- protected int mStart;
- protected int mEnd;
- protected int mCurrent;
- protected int mPrevious;
+
+ /**
+ * Lower value of the range of numbers allowed for the NumberPicker
+ */
+ private int mStart;
+
+ /**
+ * Upper value of the range of numbers allowed for the NumberPicker
+ */
+ private int mEnd;
+
+ /**
+ * Current value of this NumberPicker
+ */
+ private int mCurrent;
+
+ /**
+ * Previous value of this NumberPicker.
+ */
+ private int mPrevious;
private OnChangedListener mListener;
private Formatter mFormatter;
private long mSpeed = 300;
@@ -92,35 +125,89 @@
private boolean mIncrement;
private boolean mDecrement;
+ /**
+ * Create a new number picker
+ * @param context the application environment
+ */
public NumberPicker(Context context) {
this(context, null);
}
+ /**
+ * Create a new number picker
+ * @param context the application environment
+ * @param attrs a collection of attributes
+ */
public NumberPicker(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- @SuppressWarnings({"UnusedDeclaration"})
- public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs);
setOrientation(VERTICAL);
LayoutInflater inflater =
(LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.number_picker, this, true);
mHandler = new Handler();
+
+ OnClickListener clickListener = new OnClickListener() {
+ public void onClick(View v) {
+ validateInput(mText);
+ if (!mText.hasFocus()) mText.requestFocus();
+
+ // now perform the increment/decrement
+ if (R.id.increment == v.getId()) {
+ changeCurrent(mCurrent + 1);
+ } else if (R.id.decrement == v.getId()) {
+ changeCurrent(mCurrent - 1);
+ }
+ }
+ };
+
+ OnFocusChangeListener focusListener = new OnFocusChangeListener() {
+ public void onFocusChange(View v, boolean hasFocus) {
+
+ /* When focus is lost check that the text field
+ * has valid values.
+ */
+ if (!hasFocus) {
+ validateInput(v);
+ }
+ }
+ };
+
+ OnLongClickListener longClickListener = new OnLongClickListener() {
+ /**
+ * We start the long click here but rely on the {@link NumberPickerButton}
+ * to inform us when the long click has ended.
+ */
+ public boolean onLongClick(View v) {
+ /* The text view may still have focus so clear it's focus which will
+ * trigger the on focus changed and any typed values to be pulled.
+ */
+ mText.clearFocus();
+
+ if (R.id.increment == v.getId()) {
+ mIncrement = true;
+ mHandler.post(mRunnable);
+ } else if (R.id.decrement == v.getId()) {
+ mDecrement = true;
+ mHandler.post(mRunnable);
+ }
+ return true;
+ }
+ };
+
InputFilter inputFilter = new NumberPickerInputFilter();
mNumberInputFilter = new NumberRangeKeyListener();
mIncrementButton = (NumberPickerButton) findViewById(R.id.increment);
- mIncrementButton.setOnClickListener(this);
- mIncrementButton.setOnLongClickListener(this);
+ mIncrementButton.setOnClickListener(clickListener);
+ mIncrementButton.setOnLongClickListener(longClickListener);
mIncrementButton.setNumberPicker(this);
+
mDecrementButton = (NumberPickerButton) findViewById(R.id.decrement);
- mDecrementButton.setOnClickListener(this);
- mDecrementButton.setOnLongClickListener(this);
+ mDecrementButton.setOnClickListener(clickListener);
+ mDecrementButton.setOnLongClickListener(longClickListener);
mDecrementButton.setNumberPicker(this);
mText = (EditText) findViewById(R.id.timepicker_input);
- mText.setOnFocusChangeListener(this);
+ mText.setOnFocusChangeListener(focusListener);
mText.setFilters(new InputFilter[] {inputFilter});
mText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
@@ -129,6 +216,12 @@
}
}
+ /**
+ * Set the enabled state of this view. The interpretation of the enabled
+ * state varies by subclass.
+ *
+ * @param enabled True if this view is enabled, false otherwise.
+ */
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
@@ -137,10 +230,19 @@
mText.setEnabled(enabled);
}
+ /**
+ * Set the callback that indicates the number has been adjusted by the user.
+ * @param listener the callback, should not be null.
+ */
public void setOnChangeListener(OnChangedListener listener) {
mListener = listener;
}
+ /**
+ * Set the formatter that will be used to format the number for presentation
+ * @param formatter the formatter object. If formatter is null, String.valueOf()
+ * will be used
+ */
public void setFormatter(Formatter formatter) {
mFormatter = formatter;
}
@@ -153,10 +255,7 @@
* @param end the end of the range (inclusive)
*/
public void setRange(int start, int end) {
- mStart = start;
- mEnd = end;
- mCurrent = start;
- updateView();
+ setRange(start, end, null/*displayedValues*/);
}
/**
@@ -176,39 +275,49 @@
updateView();
}
+ /**
+ * Set the current value for the number picker.
+ *
+ * @param current the current value the start of the range (inclusive)
+ * @throws IllegalArgumentException when current is not within the range
+ * of of the number picker
+ */
public void setCurrent(int current) {
+ if (current < mStart || current > mEnd) {
+ throw new IllegalArgumentException(
+ "current should be >= start and <= end");
+ }
mCurrent = current;
updateView();
}
/**
- * The speed (in milliseconds) at which the numbers will scroll
- * when the the +/- buttons are longpressed. Default is 300ms.
+ * Sets the speed at which the numbers will scroll when the +/-
+ * buttons are longpressed
+ *
+ * @param speed The speed (in milliseconds) at which the numbers will scroll
+ * default 300ms
*/
public void setSpeed(long speed) {
mSpeed = speed;
}
- public void onClick(View v) {
- validateInput(mText);
- if (!mText.hasFocus()) mText.requestFocus();
-
- // now perform the increment/decrement
- if (R.id.increment == v.getId()) {
- changeCurrent(mCurrent + 1);
- } else if (R.id.decrement == v.getId()) {
- changeCurrent(mCurrent - 1);
- }
- }
-
private String formatNumber(int value) {
return (mFormatter != null)
? mFormatter.toString(value)
: String.valueOf(value);
}
+ /**
+ * Sets the current value of this NumberPicker, and sets mPrevious to the previous
+ * value. If current is greater than mEnd less than mStart, the value of mCurrent
+ * is wrapped around.
+ *
+ * Subclasses can override this to change the wrapping behavior
+ *
+ * @param current the new value of the NumberPicker
+ */
protected void changeCurrent(int current) {
-
// Wrap around the values if we go past the start or end
if (current > mEnd) {
current = mStart;
@@ -221,14 +330,23 @@
updateView();
}
- protected void notifyChange() {
+ /**
+ * Notifies the listener, if registered, of a change of the value of this
+ * NumberPicker.
+ */
+ private void notifyChange() {
if (mListener != null) {
mListener.onChanged(this, mPrevious, mCurrent);
}
}
- protected void updateView() {
-
+ /**
+ * Updates the view of this NumberPicker. If displayValues were specified
+ * in {@link #setRange}, the string corresponding to the index specified by
+ * the current value will be returned. Otherwise, the formatter specified
+ * in {@link setFormatter} will be used to format the number.
+ */
+ private void updateView() {
/* If we don't have displayed values then use the
* current number else find the correct value in the
* displayed values for the current number.
@@ -253,16 +371,6 @@
updateView();
}
- public void onFocusChange(View v, boolean hasFocus) {
-
- /* When focus is lost check that the text field
- * has valid values.
- */
- if (!hasFocus) {
- validateInput(v);
- }
- }
-
private void validateInput(View v) {
String str = String.valueOf(((TextView) v).getText());
if ("".equals(str)) {
@@ -277,30 +385,15 @@
}
/**
- * We start the long click here but rely on the {@link NumberPickerButton}
- * to inform us when the long click has ended.
+ * @hide
*/
- public boolean onLongClick(View v) {
-
- /* The text view may still have focus so clear it's focus which will
- * trigger the on focus changed and any typed values to be pulled.
- */
- mText.clearFocus();
-
- if (R.id.increment == v.getId()) {
- mIncrement = true;
- mHandler.post(mRunnable);
- } else if (R.id.decrement == v.getId()) {
- mDecrement = true;
- mHandler.post(mRunnable);
- }
- return true;
- }
-
public void cancelIncrement() {
mIncrement = false;
}
+ /**
+ * @hide
+ */
public void cancelDecrement() {
mDecrement = false;
}
@@ -404,9 +497,26 @@
}
/**
+ * Returns the current value of the NumberPicker
* @return the current value.
*/
public int getCurrent() {
return mCurrent;
}
+
+ /**
+ * Returns the upper value of the range of the NumberPicker
+ * @return the uppper number of the range.
+ */
+ protected int getEndRange() {
+ return mEnd;
+ }
+
+ /**
+ * Returns the lower value of the range of the NumberPicker
+ * @return the lower number of the range.
+ */
+ protected int getBeginRange() {
+ return mStart;
+ }
}
diff --git a/common/java/com/android/common/widget/NumberPickerButton.java b/core/java/android/widget/NumberPickerButton.java
similarity index 91%
rename from common/java/com/android/common/widget/NumberPickerButton.java
rename to core/java/android/widget/NumberPickerButton.java
index f6b6d5d..1c8579c 100644
--- a/common/java/com/android/common/widget/NumberPickerButton.java
+++ b/core/java/android/widget/NumberPickerButton.java
@@ -14,20 +14,22 @@
* limitations under the License.
*/
-package com.android.common.widget;
+package android.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.widget.ImageButton;
+import android.widget.NumberPicker;
import com.android.internal.R;
/**
- * This class exists purely to cancel long click events.
+ * This class exists purely to cancel long click events, that got
+ * started in NumberPicker
*/
-public class NumberPickerButton extends ImageButton {
+class NumberPickerButton extends ImageButton {
private NumberPicker mNumberPicker;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 12e8e29..7ba0fa1 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6301,7 +6301,8 @@
if (DEBUG_EXTRACT) Log.v(TAG, "beforeTextChanged start=" + start
+ " before=" + before + " after=" + after + ": " + buffer);
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()
+ && !isPasswordInputType(mInputType)) {
mBeforeText = buffer.toString();
}
@@ -6972,9 +6973,7 @@
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- boolean isPassword =
- (mInputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION)) ==
- (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
+ final boolean isPassword = isPasswordInputType(mInputType);
if (!isPassword) {
CharSequence text = getText();
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index b87e278..caed308 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -23,9 +23,9 @@
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.NumberPicker;
import com.android.internal.R;
-import com.android.common.widget.NumberPicker;
import java.text.DateFormatSymbols;
import java.util.Calendar;
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index bea009c0..3df419a 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -546,6 +546,11 @@
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
+ if (event.getPointerCount() > 1) {
+ // ZoomButtonsController doesn't handle mutitouch. Give up control.
+ return false;
+ }
+
if (mReleaseTouchListenerOnUp) {
// The controls were dismissed but we need to throw away all events until the up
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index adafbb4..eb6d1a6 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -145,20 +145,6 @@
void onGrabbedStateChange(View v, int grabbedState);
}
- // TODO: For debugging; remove after glitches debugged.
- @Override
- protected void dispatchDraw(Canvas canvas) {
- int orientation = getResources().getConfiguration().orientation;
- if (mOrientation == HORIZONTAL && orientation != Configuration.ORIENTATION_PORTRAIT
- || mOrientation == VERTICAL && orientation != Configuration.ORIENTATION_LANDSCAPE) {
- // UBER HACK ALERT. This is a workaround for a configuration race condition between
- // orientation changed notification and the resize notification. This just prevents
- // us from drawing under this circumstance, though the view will still be wrong.
- return;
- }
- super.dispatchDraw(canvas);
- }
-
/**
* Simple container class for all things pertinent to a slider.
* A slider consists of 3 Views:
@@ -433,7 +419,7 @@
/**
* Start animating the slider. Note we need two animations since an Animator
* keeps internal state of the invalidation region which is just the view being animated.
- *
+ *
* @param anim1
* @param anim2
*/
@@ -671,7 +657,7 @@
resetView();
}
anim.setAnimationListener(mAnimationDoneListener);
-
+
/* Animation can be the same for these since the animation just holds */
mLeftSlider.startAnimation(anim, anim);
mRightSlider.startAnimation(anim, anim);
diff --git a/core/java/com/android/internal/widget/WeightedLinearLayout.java b/core/java/com/android/internal/widget/WeightedLinearLayout.java
new file mode 100644
index 0000000..b90204e
--- /dev/null
+++ b/core/java/com/android/internal/widget/WeightedLinearLayout.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 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.internal.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.widget.LinearLayout;
+
+import static android.view.View.MeasureSpec.*;
+import static com.android.internal.R.*;
+
+/**
+ * A special layout when measured in AT_MOST will take up a given percentage of
+ * the available space.
+ */
+public class WeightedLinearLayout extends LinearLayout {
+ private float mMajorWeight;
+ private float mMinorWeight;
+
+ public WeightedLinearLayout(Context context) {
+ super(context);
+ }
+
+ public WeightedLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a =
+ context.obtainStyledAttributes(attrs, styleable.WeightedLinearLayout);
+
+ mMajorWeight = a.getFloat(styleable.WeightedLinearLayout_majorWeight, 0.0f);
+ mMinorWeight = a.getFloat(styleable.WeightedLinearLayout_minorWeight, 0.0f);
+
+ a.recycle();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+ final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
+
+ final int widthMode = getMode(widthMeasureSpec);
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ int width = getMeasuredWidth();
+ int height = getMeasuredHeight();
+ boolean measure = false;
+
+ final int widthSize = getSize(widthMeasureSpec);
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, EXACTLY);
+
+ final float widthWeight = isPortrait ? mMinorWeight : mMajorWeight;
+ if (widthMode == AT_MOST && widthWeight > 0.0f) {
+ if (width < (widthSize * widthWeight)) {
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec((int) (widthSize * widthWeight),
+ EXACTLY);
+ measure = true;
+ }
+ }
+
+ // TODO: Support height?
+
+ if (measure) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+}
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index d2e9454..4e1ae62 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -382,7 +382,7 @@
dbus_connection_unregister_object_path(nat->conn, agent_path);
dbus_bus_remove_match(nat->conn,
- "type='signal',interface='org.bluez.audio.Sink'",
+ "type='signal',interface='org.bluez.AudioSink'",
&err);
if (dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index 409dcd3..231e11c 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -18,7 +18,8 @@
*/
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.internal.widget.WeightedLinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/parentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -27,7 +28,8 @@
android:paddingBottom="3dip"
android:paddingLeft="3dip"
android:paddingRight="1dip"
- >
+ android:majorWeight="0.5"
+ android:minorWeight="0.8">
<LinearLayout android:id="@+id/topPanel"
android:layout_width="match_parent"
@@ -109,7 +111,8 @@
android:orientation="horizontal"
android:paddingTop="4dip"
android:paddingLeft="2dip"
- android:paddingRight="2dip" >
+ android:paddingRight="2dip"
+ android:useLargestChild="true">
<LinearLayout android:id="@+id/leftSpacer"
android:layout_weight="0.25"
android:layout_width="0dip"
@@ -142,4 +145,4 @@
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
-</LinearLayout>
+</com.android.internal.widget.WeightedLinearLayout>
diff --git a/core/res/res/layout/date_picker.xml b/core/res/res/layout/date_picker.xml
index 56d5494..4fd46b3 100644
--- a/core/res/res/layout/date_picker.xml
+++ b/core/res/res/layout/date_picker.xml
@@ -29,7 +29,7 @@
android:layout_height="wrap_content">
<!-- Month -->
- <com.android.common.widget.NumberPicker
+ <NumberPicker
android:id="@+id/month"
android:layout_width="80dip"
android:layout_height="wrap_content"
@@ -40,7 +40,7 @@
/>
<!-- Day -->
- <com.android.common.widget.NumberPicker
+ <NumberPicker
android:id="@+id/day"
android:layout_width="80dip"
android:layout_height="wrap_content"
@@ -51,7 +51,7 @@
/>
<!-- Year -->
- <com.android.common.widget.NumberPicker
+ <NumberPicker
android:id="@+id/year"
android:layout_width="95dip"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout/number_picker.xml b/core/res/res/layout/number_picker.xml
index 44dca33..9241708 100644
--- a/core/res/res/layout/number_picker.xml
+++ b/core/res/res/layout/number_picker.xml
@@ -19,7 +19,7 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <com.android.common.widget.NumberPickerButton android:id="@+id/increment"
+ <NumberPickerButton android:id="@+id/increment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/timepicker_up_btn" />
@@ -34,7 +34,7 @@
android:textSize="30sp"
android:background="@drawable/timepicker_input" />
- <com.android.common.widget.NumberPickerButton android:id="@+id/decrement"
+ <NumberPickerButton android:id="@+id/decrement"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/timepicker_down_btn" />
diff --git a/core/res/res/layout/time_picker.xml b/core/res/res/layout/time_picker.xml
index b9f42ce..6124ea8 100644
--- a/core/res/res/layout/time_picker.xml
+++ b/core/res/res/layout/time_picker.xml
@@ -26,7 +26,7 @@
android:layout_height="wrap_content">
<!-- hour -->
- <com.android.common.widget.NumberPicker
+ <NumberPicker
android:id="@+id/hour"
android:layout_width="70dip"
android:layout_height="wrap_content"
@@ -35,7 +35,7 @@
/>
<!-- minute -->
- <com.android.common.widget.NumberPicker
+ <NumberPicker
android:id="@+id/minute"
android:layout_width="70dip"
android:layout_height="wrap_content"
diff --git a/core/res/res/values-land/donottranslate.xml b/core/res/res/values-land/donottranslate.xml
new file mode 100644
index 0000000..75a7b06
--- /dev/null
+++ b/core/res/res/values-land/donottranslate.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- @hide DO NOT TRANSLATE. Workaround for resource race condition in lockscreen -->
+ <bool name="lockscreen_isPortrait">false</bool>
+</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8dadd88..287e3a0 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1700,6 +1700,10 @@
space by giving it a layout_weight of 0.5 and setting the weightSum
to 1.0. -->
<attr name="weightSum" format="float" />
+ <!-- When set to true, all children with a weight will be considered having
+ the minimum size of the largest child. If false, all children are
+ measured normally. -->
+ <attr name="useLargestChild" format="boolean" />
</declare-styleable>
<declare-styleable name="ListView">
<!-- Reference to an array resource that will populate the ListView. For static content,
@@ -2330,6 +2334,11 @@
<attr name="orientation" />
</declare-styleable>
+ <!-- @hide -->
+ <declare-styleable name="WeightedLinearLayout">
+ <attr name="majorWeight" format="float" />
+ <attr name="minorWeight" format="float" />
+ </declare-styleable>
<!-- ========================= -->
<!-- Drawable class attributes -->
diff --git a/core/res/res/values/donottranslate.xml b/core/res/res/values/donottranslate.xml
index 6def3bf..78d4d36d 100644
--- a/core/res/res/values/donottranslate.xml
+++ b/core/res/res/values/donottranslate.xml
@@ -20,4 +20,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Default text encoding for WebSettings. -->
<string name="default_text_encoding">Latin-1</string>
+ <!-- @hide DO NOT TRANSLATE. Workaround for resource race condition in lockscreen. -->
+ <bool name="lockscreen_isPortrait">true</bool>
</resources>
diff --git a/docs/html/guide/developing/debug-tasks.jd b/docs/html/guide/developing/debug-tasks.jd
index 3279741..975f6998 100644
--- a/docs/html/guide/developing/debug-tasks.jd
+++ b/docs/html/guide/developing/debug-tasks.jd
@@ -6,7 +6,8 @@
<h2>In this document</h2>
<ol>
<li><a href="#tools">Tools</a></li>
- <li><a href="#additionaldebugging">Debug and Test Settings</a></li>
+ <li><a href="#additionaldebugging">Debug with Dev Tools</a></li>
+ <li><a href="#DebuggingWebPages">Debugging Web Pages</a></li>
<li><a href="#toptips">Top Debugging Tips</a></li>
<li><a href="#ide-debug-port">Configuring Your IDE to Attach to the Debugging Port</a></li>
</ol>
@@ -17,178 +18,242 @@
<h2 id="tools">Tools</h2>
-<p>The Android SDK includes a fairly extensive set of tools to help you debug your programs: </p>
-<ul>
- <li><a href="{@docRoot}guide/developing/tools/ddms.html"><strong>DDMS</strong></a> - A graphical program that
+
+<p>The Android SDK includes a set of tools to help you debug and profile
+your applications. Here are some tools that you'll use most often:</p>
+
+<dl>
+ <dt><strong><a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge
+(ADB)</a></strong></dt>
+ <dd>Provides various device management capabilities, including
+ moving and syncing files to the emulator, forwarding ports, and running a UNIX
+ shell on the emulator.</dd>
+ <dt><strong><a href="{@docRoot}guide/developing/tools/ddms.html">Dalvik Debug Monitor Server
+(DDMS)</a></strong></dt>
+ <dd>A graphical program that
supports port forwarding (so you can set up breakpoints in your code in your
IDE), screen captures on the emulator, thread and stack information,
- and many other features. You can also run logcat to retrieve your Log messages.
- See the linked topic for more information. </li>
- <li><strong><a href="{@docRoot}guide/developing/tools/ddms.html#logcat">logcat</a></strong> - Dumps a log of system
- messages. The messages include a stack trace when the emulator throws an error,
- as well as Log messages. To run logcat, see the linked topic.
-
- <pre>...
-I/MemoryDealer( 763): MemoryDealer (this=0x54bda0): Creating 2621440 bytes heap at 0x438db000
-<span style="background-color:#CCCCCC; border-bottom:medium">I/Logger( 1858): getView() requesting item number 0
-I/Logger( 1858): getView() requesting item number 1
-I/Logger( 1858): getView() requesting item number 2</span>
-D/ActivityManager( 763): Stopping: HistoryRecord{409dbb20 com.android.home.AllApps}
-...</pre>
+ and many other features. You can also run logcat to retrieve your Log messages.</dd>
+ </dd>
+ <dt><strong><a href="{@docRoot}guide/developing/tools/traceview.html">Traceview</a></strong></dt>
+ <dd>A graphical viewer that displays trace file data for method calls and times saved by
+ your application, which can help you profile the performance of your application.</dd>
+ <dt><strong><a href="{@docRoot}guide/developing/tools/ddms.html#logcat">logcat</a></strong></dt>
+ <dd>Dumps a log of system
+ messages. The messages include a stack trace when the emulator throws an error,
+ as well as {@link android.util.Log} messages you've written from your application. To run
+ logcat, execute <code>adb logcat</code> or, from DDMS, select <strong>Device > Run
+ logcat</strong>.
+ <p>{@link android.util.Log} is a logging
+ class you can use to print out messages to the logcat. You can read messages
+ in real time if you run logcat on DDMS (covered next). Common logging methods include:
+ {@link android.util.Log#v(String,String)} (verbose), {@link
+ android.util.Log#d(String,String)} (debug), {@link android.util.Log#i(String,String)}
+ (information), {@link android.util.Log#w(String,String)} (warning) and {@link
+ android.util.Log#e(String,String)} (error). For example:</p>
+<pre class="no-pretty-print">
+Log.i("MyActivity", "MyClass.getView() — get item number " + position);
+</pre>
+ <p>The logcat will then output something like:</p>
+<pre class="no-pretty-print">
+I/MyActivity( 1557): MyClass.getView() — get item number 1
+</pre>
+ <p>Logcat is also the place to look when debugging a web page in the Android browser. All
+browser bugs will be output to logcat with the {@code WebCore} tag.
+</dl>
- </li>
- <li><p><strong>{@link android.util.Log Android Log}</strong>- A logging
- class to print out messages to a log file on the emulator. You can read messages
- in real time if you run logcat on DDMS (covered next). Add a few logging
- method calls to your code.</p>
- <p>To use the <code>Log</code> class, you just call <code>Log.v()</code>
- (verbose), <code>Log.d()</code> (debug), <code>Log.i()</code> (information),
- <code>Log.w()</code> (warning) or <code>Log.e</code> (error) depending
- on the importance you wish to assign the log message.</p>
- <code>Log.i("MyActivity", "MyClass.getView()
- — Requesting item number " + position)</code>
- <p>You can use logcat to read these messages</p></li>
- <li><strong><a href="{@docRoot}guide/developing/tools/traceview.html">Traceview</a> </strong>- Android can save
- a log of method calls and times to a logging file that you can view in a
- graphical reader called Traceview. See the linked topic for more information. </li>
-</ul>
-<ul>
- <li><a href="{@docRoot}guide/developing/eclipse-adt.html"><strong>Eclipse plugin</strong></a> - The ADT Plugin
- for Eclipse integrates a number of these tools (ADB, DDMS, logcat output,
- and other functionality). See the linked topic for more information. </li>
- <li><strong>Debug and Test Device Settings</strong> - Android exposes several settings
- that expose useful information such as CPU usage and frame rate. See <a href="#additionaldebugging">Debug
- and Test Settings on the Emulator</a> below. </li>
-</ul>
-<p>Also, see the <a href="{@docRoot}resources/faq/troubleshooting.html">Troubleshooting</a> section
- of the doc to figure out why your application isn't appearing on the emulator,
- or why it's not starting. </p>
+<p>For more information about all the development tools provided with the Android SDK, see the <a
+href="{@docRoot}guide/developing/tools/index.html">Tools</a> document.</p>
+<p>In addition to the above tools, you may also find the following useful for debugging:
+<dl>
+ <dt><a href="{@docRoot}guide/developing/eclipse-adt.html"><strong>Eclipse ADT
+plugin</strong></a></dt>
+ <dd>The ADT Plugin for Eclipse integrates a number of the Android development tools (ADB, DDMS,
+logcat output, and other functionality), so that you won't work with them directly but will utilize
+them through the Eclipse IDE.</dd>
+ <dt><strong>Developer Settings in the Dev Tools app</strong></dt>
+ <dd>The Dev Tools application included in the emulator system image exposes several settings
+ that provide useful information such as CPU usage and frame rate. See <a
+href="#additionaldebugging">Debugging and Testing with Dev Tools</a> below.</dd>
+</dl>
-<h2 id="additionaldebugging">Debug and Test Settings</h2>
+<h2 id="additionaldebugging">Debugging and Testing with Dev Tools</h2>
-<p>With the <strong>Dev Tools</strong> application, you can turn on a number of settings that will make it easier to test
- and debug your applications. To get to the development settings page on the emulator, launch the
- <strong>Dev Tools</strong> application and open <strong>Development Settings</strong>.
- This will open the development settings page with the following options (among
- others):</p>
-<ul>
- <li><strong>Debug app</strong> Selects the application that
- will be debugged. You do not need to set this to attach a debugger, but setting
- this value has two effects:
+<p>With the Dev Tools application, you can turn on a number of settings that will
+make it easier to test and debug your applications. The Dev Tools application is automatically
+installed on all system images included with the SDK. The source code for the Dev Tools application
+is also provided in the SDK samples so that you may build it and then install the application on any
+development device.</p>
+
+<p>To get to the development settings page on the emulator, launch the Dev Tools application and
+select Development Settings. This will open the Development Settings page with the
+following options (among others):</p>
+
+<dl>
+ <dt><strong>Debug app</strong></dt>
+ <dd>Lets you select the application to debug. You do not need to set this to attach a debugger,
+ but setting this value has two effects:
<ul>
- <li>It will prevent Android from throwing an error if you pause on
+ <li>It will prevent Android from throwing an error if you pause on
a breakpoint for a long time while debugging.</li>
<li>It will enable you to select the <em>Wait for Debugger</em> option
to pause application startup until your debugger attaches (described
next). </li>
</ul>
- </li>
- <li><strong>Wait for debugger </strong>
- Blocks the selected application from loading until a debugger attaches. This
+ </dd>
+ <dt><strong>Wait for debugger</strong></dt>
+ <dd>Blocks the selected application from loading until a debugger attaches. This
way you can set a breakpoint in onCreate(), which is important to debug
the startup process of an Activity. When you change this option, any
currently running instances of the selected application will be killed.
In order to check this box, you must have selected a debug application
as described in the previous option. You can do the same thing by adding
- {@link android.os.Debug#waitForDebugger()} to your code. </li>
- <li><strong>Immediately destroy activities</strong> Tells the
+ {@link android.os.Debug#waitForDebugger()} to your code.</dd>
+ <dt><strong>Show screen updates</strong></dt>
+ <dd>Flashes a momentary pink rectangle on any screen sections that are being
+ redrawn. This is very useful for discovering unnecessary screen drawing.</dd>
+ <dt><strong>Immediately destroy activities</strong></dt>
+ <dd>Tells the
system to destroy an activity as soon as it is stopped (as if Android had to
reclaim memory). This is very useful for testing the {@link android.app.Activity#onSaveInstanceState}
/ {@link android.app.Activity#onCreate(android.os.Bundle)} code path, which would
otherwise be difficult to force. Choosing this option will probably reveal
- a number of problems in your application due to not saving state.</li>
- <li><strong>Show screen updates</strong>
- Flashes a momentary pink rectangle on any screen sections that are being
- redrawn. This is very useful for discovering unnecessary screen drawing. </li>
- <li><strong>Show CPU usage</strong> Displays CPU meters at the
+ a number of problems in your application due to not saving state.</dd>
+ <dt><strong>Show CPU usage</strong></dt>
+ <dd>Displays CPU meters at the
top of the screen, showing how much the CPU is being used. The top red bar
shows overall CPU usage, and the green bar underneath it shows the CPU time
spent in compositing the screen. <em>Note: You cannot turn this feature off
- once it is on, without restarting the emulator.</em> </li>
- <li><strong>Show background</strong> Displays a background pattern
+ once it is on, without restarting the emulator.</em> </dd>
+ <dt><strong>Show background</strong></dt>
+ <dd>Displays a background pattern
when no activity screens are visible. This typically does not happen, but
- can happen during debugging. </li>
-</ul>
+ can happen during debugging.</dd>
+</dl>
+
<p>These settings will be remembered across emulator restarts. </p>
+<h2 id="DebuggingWebPages">Debugging Web Pages</h2>
+
+<p>If you're developing a web application for Android devices, you can debug your JavaScript on
+Android using the Console APIs, which will output messages to logcat. If you're familiar
+debugging web pages with Firefox's FireBug or WebKit's Web Inspector, then you're probably familiar
+with the Console APIs. The Android Browser (and {@link android.webkit.WebChromeClient}) supports
+most of the same APIs.</p>
+
+<p>When you call a function from the Console APIs (in the DOM's {@code window.console} object),
+you will see the output in logcat as a warning. For example, if your web page
+executes the following JavaScript:</p>
+<pre class="no-pretty-print">
+console.log("Hello World");
+</pre>
+<p>Then the logcat output from the Android Browser will look like this:</p>
+<pre class="no-pretty-print">
+W/browser ( 202): Console: Hello World :0
+</pre>
+
+<p class="note"><strong>Note:</strong> All Console messages from the Android
+Browser are tagged with the name "browser" on Android platforms running API Level 7 or higher and
+tagged with the name "WebCore" for platforms running API Level 6 or lower.</p>
+
+<p>Not all of the Console APIs available in Firefox or other WebKit browsers are implemented
+on Android. Mostly, you need to depend on basic text logging provided by
+functions like {@code console.log(String)}, {@code console.info(String)}, {@code
+console.warn(String)}, and {@code console.error(String)}. Although other Console functions may not
+be implemented, they will not raise run-time errors, but will simply not behave as you might
+expect.</p>
+
+<p>If you've implemented a custom {@link android.webkit.WebView} in your application, then in order
+to receive messages that are sent through the Console APIs, you must provide a {@link
+android.webkit.WebChromeClient} that implements the {@link
+android.webkit.WebChromeClient#onConsoleMessage(String,int,String) onConsoleMessage()} callback
+method. For example, assuming that the {@code myWebView} field references the {@link
+android.webkit.WebView} in your application, you can log debug messages like this:</p>
+<pre>
+myWebView.setWebChromeClient(new WebChromeClient() {
+ public void onConsoleMessage(String message, int lineNumber, String sourceID) {
+ Log.d("MyApplication", message);
+ }
+});
+</pre>
+<p>The {@link android.webkit.WebChromeClient#onConsoleMessage(String,int,String)
+onConsoleMessage()} method will be called each time one of the Console methods is called from
+within your {@link android.webkit.WebView}.</p>
+<p>When the "Hello World" log is executed through your {@link android.webkit.WebView}, it will
+now look like this:</p>
+<pre class="no-pretty-print">
+D/MyApplication ( 430): Hello World
+</pre>
+
+<p class="note"><strong>Note:</strong> The {@link
+android.webkit.WebChromeClient#onConsoleMessage(String,int,String) onConsoleMessage()} callback
+method was added with API Level 7. If you are targetting platforms running API Level 6 or lower,
+then your Console messages will automatically be sent to logcat with the "WebCore" logging tag.</p>
+
+
+
+
<h2 id="toptips">Top Debugging Tips</h2>
-<!--
-<ul>
- <li><a href="#stackdump">Quick stack dump</a></li>
- <li><a href="#displayinfo">Displaying useful info on the emulator screen </a></li>
- <li><a href="#dumpstate">Getting system state information from the emulator (dumpstate)</a></li>
- <li><a href="#dumpsys">Getting application state information from the emulator (dumpsys)</a></li>
- <li><a href="#radioinfo">Getting wireless connectivity information</a></li>
- <li><a href="#loggingdata">Logging Trace Data</a></li>
- <li><a href="#logradio">Logging Radio Data </a></li>
- <li><a href="#adb">Running adb</a></li>
- <li><a href="#screencaps">Getting screen captures from the emulator</a></li>
- <li><a href="#debughelpers">Using debug helper classes</a></li>
-</ul>
--->
+
<dl>
-<dt>Quick stack dump <a name="stackdump" id="stackdump"></a></dt>
+<dt><strong>Dump the stack trace</strong></dt>
<dd>To obtain a stack dump from emulator, you can log
in with <code>adb shell</code>, use "ps" to find the process you
want, and then "kill -3 ". The stack trace appears in the log file.
</dd>
-<dt>Displaying useful info on the emulator screen<a name="displayinfo" id="displayinfo"></a></dt>
+<dt><strong>Display useful info on the emulator screen</strong></dt>
<dd>The device can display useful information such as CPU usage or highlights
around redrawn areas. Turn these features on and off in the developer settings
window as described in <a href="#additionaldebugging">Setting debug and test
configurations on the emulator</a>.
</dd>
-<dt>Getting system state information from the emulator (dumpstate)<a name="dumpstate" id="dumpstate"></a> </dt>
+<dt><strong>Get system state information from the emulator (dumpstate)</strong></dt>
<dd>You can access dumpstate information from the Dalvik Debug Monitor Service
tool. See <a href="{@docRoot}guide/developing/tools/adb.html#dumpsys">dumpsys and
dumpstate</a> on the adb topic page.</dd>
-<dt>Getting application state information from the emulator (dumpsys)<a name="dumpsys" id="dumpsys"></a></dt>
+<dt><strong>Get application state information from the emulator (dumpsys)</strong></dt>
<dd>You can access dumpsys information from the Dalvik Debug Monitor Service
tool. See <a href="{@docRoot}guide/developing/tools/adb.html#dumpsys">dumpsys and
dumpstate</a> on the adb topic page.</dd>
-<dt>Getting wireless connectivity information <a name="radioinfo" id="radioinfo"></a></dt>
+<dt><strong>Get wireless connectivity information</strong></dt>
<dd>You can get information about wireless connectivity using the Dalvik Debug
Monitor Service tool. From the <strong>Device</strong> menu, select "Dump
radio state".</dd>
-<dt>Logging Trace Data<a name="loggingdata" id="loggingdata"></a></dt>
+<dt><strong>Log trace data</strong></dt>
<dd>You can log method calls and other tracing data in an activity by calling
-android.os.Debug.startMethodTracing(). See <a
+{@link android.os.Debug#startMethodTracing(String) startMethodTracing()}. See <a
href="{@docRoot}guide/developing/tools/traceview.html">Running the Traceview Debugging
Program</a> for details. </dd>
-<dt>Logging Radio Data<a name="logradio" id="logradio"></a></dt>
+<dt><strong>Log radio data</strong></dt>
<dd>By default, radio information is not logged to the system (it is a lot of
data). However, you can enable radio logging using the following commands:
-<pre>
+<pre class="no-pretty-print">
adb shell
logcat -b radio
</pre>
</dd>
-<dt>Running adb<a name="adb" id="adb"></a></dt>
-<dd>Android ships with a tool called adb that provides various capabilities, including
-moving and syncing files to the emulator, forwarding ports, and running a UNIX
-shell on the emulator. See <a href="{@docRoot}guide/developing/tools/adb.html">Using adb</a> for details.</dd>
+<dt><strong>Capture screenshots</strong></dt>
+<dd>The Dalvik Debug Monitor Server (DDMS) can capture screenshots from the emulator. Select
+<strong>Device > Screen capture</strong>.</dd>
-<dt>Getting screen captures from the emulator<a name="screencaps" id="screencaps"></a></dt>
-<dd> Dalvik Debug Monitor Server (DDMS) can capture screenshots from the emulator.</dd>
-
-
-<a name="debughelpers"></a>
-
-<dt>Using debugging helper classes</dt>
-
+<dt><strong>Use debugging helper classes</strong></dt>
<dd>Android provides debug helper classes such as {@link android.util.Log
util.Log} and {@link android.os.Debug} for your convenience. </dd>
</dl>
+<p>Also see the <a href="{@docRoot}resources/faq/troubleshooting.html">Troubleshooting</a> document
+for answers to some common developing and debugging issues.</p>
+
+
<h2 id="ide-debug-port">Configuring Your IDE to Attach to the Debugging Port</h2>
<p>DDMS will assign a specific debugging port to every virtual machine that it
diff --git a/docs/html/resources/tutorials/views/hello-autocomplete.jd b/docs/html/resources/tutorials/views/hello-autocomplete.jd
index fba1ad8..e26683d 100644
--- a/docs/html/resources/tutorials/views/hello-autocomplete.jd
+++ b/docs/html/resources/tutorials/views/hello-autocomplete.jd
@@ -1,56 +1,82 @@
-page.title=Hello, AutoCompleteTextView
+page.title=Auto Complete
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>{@link android.widget.AutoCompleteTextView} is an implementation of the EditText widget that will provide
-auto-complete suggestions as the user types. The suggestions are extracted from a collection of strings.</p>
+<p>To create a text entry widget that provides auto-complete suggestions, use
+the {@link android.widget.AutoCompleteTextView} widget. Suggestions are received from a
+collection of strings associated with the widget through an {@link
+android.widget.ArrayAdapter}.</p>
+
+<p>In this tutorial, you will create a {@link android.widget.AutoCompleteTextView} widget that
+provides suggestions for a country name.</p>
<ol>
- <li>Start a new project/Activity called HelloAutoComplete.</li>
- <li>Open the layout file.
- Make it like so:
+ <li>Start a new project named <em>HelloAutoComplete</em>.</li>
+ <li>Create an XML file named <code>list_item.xml</code> and save it inside the
+<code>res/layout/</code> folder. Edit the file to look like this:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="10dp"
+ android:textSize="16sp"
+ android:textColor="#000">
+</TextView>
+</pre>
+ <p>This file defines a simple {@link android.widget.TextView} that will be used for each
+item that appears in the list of suggestions.</p>
+ </li>
+ <li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
+ android:layout_height="wrap_content"
+ android:padding="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Country" />
-
- <AutoCompleteTextView android:id="@+id/edit"
+ <AutoCompleteTextView android:id="@+id/autocomplete_country"
android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
-
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="5dp"/>
</LinearLayout>
</pre>
+ <p>The {@link android.widget.TextView} is a label that introduces the {@link
+android.widget.AutoCompleteTextView} widget.
</li>
-<li>Open HelloAutoComplete.java and insert the following as the <code>onCreate</code> method:
+<li>Open <code>HelloAutoComplete.java</code> and insert the following code for the {@link
+android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
- AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.edit);
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
- android.R.layout.simple_dropdown_item_1line, COUNTRIES);
+ AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.autocomplete_country);
+ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, COUNTRIES);
textView.setAdapter(adapter);
}
</pre>
- <p>Here, we create an AutoComplteteTextView from our layout. We then
- create an {@link android.widget.ArrayAdapter} that binds a <code>simple_dropdown_item_1line</code>
- layout item to each entry in the <code>COUNTRIES</code> array (which we'll add next).
- The last part sets the ArrayAdapter to associate with our AutoCompleteTextView.</p>
+
+<p>After the content view is set to the <code>main.xml</code> layout, the {@link
+android.widget.AutoCompleteTextView} widget is captured from the layout with {@link
+android.app.Activity#findViewById(int)}. A new {@link
+android.widget.ArrayAdapter} is then initialized to bind the <code>list_item.xml</code> layout
+to each list item in the <code>COUNTRIES</code> string array (defined in the next step).
+Finally, {@link android.widget.AutoCompleteTextView#setAdapter(T) setAdapter()} is called to
+associate the {@link android.widget.ArrayAdapter} with the
+{@link android.widget.AutoCompleteTextView} widget so that the string array will populate
+the list of suggestions.</p>
</li>
-<li>After the <code>onCreate()</code> method, add the String array:
+<li>Inside the <code>HelloAutoComplete</code> class, add the string array:
<pre>
static final String[] COUNTRIES = new String[] {
"Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
@@ -96,19 +122,50 @@
"Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
};
</pre>
- <p>This is the list of suggestions that will be offered as the user types into the
- AutoCompleteTextView.</p>
+<p>This is the list of suggestions that will be provided in a drop-down list when the user types into
+the {@link android.widget.AutoCompleteTextView} widget.</p>
</li>
-<li>Now run it.</li>
+<li>Run the application.</li>
</ol>
<p>As you type, you should see something like this:</p>
<img src="images/hello-autocomplete.png" width="150px" />
+<h2>More Information</h2>
+
+<p>Note that using a hard-coded string array is not a recommended design practice because your
+application code should focus on behavior, not content. Application content such as strings
+should be externalized from the code in order to make modifications to the content easier and
+facilitate localization of the content. The hard-coded strings are used in this tutorial only to
+make it simple and focus on the {@link android.widget.AutoCompleteTextView} widget.
+Instead, your application should declare such string arrays in an XML file. This can be done
+with a {@code <string-array<} resource in your project {@code res/values/strings.xml} file.
+For example:</p>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string-array name="countries_array">
+ <item>Bahrain</item>
+ <item>Bangladesh</item>
+ <item>Barbados</item>
+ <item>Belarus</item>
+ <item>Belgium</item>
+ <item>Belize</item>
+ <item>Benin</item>
+ </string-array>
+</resources>
+</pre>
+<p>To use these resource strings for the {@link android.widget.ArrayAdapter}, replace the original
+{@link android.widget.ArrayAdapter} constructor line with the following:</p>
+<pre>
+String[] countries = getResources().getStringArray(R.array.countries_array);
+ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, countries);
+</pre>
+
+
<h3>References</h3>
<ul>
- <li>{@link android.R.layout}</li>
<li>{@link android.widget.ArrayAdapter}</li>
<li>{@link android.widget.AutoCompleteTextView}</li>
</ul>
diff --git a/docs/html/resources/tutorials/views/hello-datepicker.jd b/docs/html/resources/tutorials/views/hello-datepicker.jd
index fcd43f3..c50d650 100644
--- a/docs/html/resources/tutorials/views/hello-datepicker.jd
+++ b/docs/html/resources/tutorials/views/hello-datepicker.jd
@@ -1,52 +1,57 @@
-page.title=Hello, DatePicker
+page.title=Date Picker
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>A {@link android.widget.DatePicker} is a widget that allows the user to select a month, day and year.</p>
+<p>To provide a widget for selecting a date, use the {@link android.widget.DatePicker}
+widget, which allows the user to select the month, day, and year, in a familiar interface.</p>
+<p>In this tutorial, you'll create a {@link android.app.DatePickerDialog}, which presents the
+date picker in a floating dialog box at the press of a button. When the date is set by
+the user, a {@link android.widget.TextView} will update with the new date.</p>
<ol>
- <li>Start a new project/Activity called HelloDatePicker.</li>
- <li>Open the layout file and make it like so:
- <pre>
+ <li>Start a new project named <em>HelloDatePicker</em>.</li>
+ <li>Open the <code>res/layout/main.xml</code> file and insert the following:
+<pre>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
-
<TextView android:id="@+id/dateDisplay"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text=""/>
-
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""/>
<Button android:id="@+id/pickDate"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Change the date"/>
-
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Change the date"/>
</LinearLayout>
</pre>
- <p>For the layout, we're using a vertical LinearLayout, with a {@link android.widget.TextView} that
- will display the date and a {@link android.widget.Button} that will initiate the DatePicker dialog.
- With this layout, the TextView will sit above the Button.
- The text value in the TextView is set empty, as it will be filled
- with the current date when our Activity runs.</p>
- </li>
+ <p>This creates a basic {@link android.widget.LinearLayout} with a {@link android.widget.TextView}
+ that will display the date and a {@link android.widget.Button} that will open the {@link
+ android.app.DatePickerDialog}.</p>
+ </li>
- <li>Open HelloDatePicker.java. Insert the following to the HelloDatePicker class:
+ <li>Open <code>HelloDatePicker.java</code> and add the following members to the class:
<pre>
private TextView mDateDisplay;
private Button mPickDate;
-
private int mYear;
private int mMonth;
private int mDay;
static final int DATE_DIALOG_ID = 0;
+</pre>
+ <p>The first group of members define variables for the layout {@link android.view.View}s and the
+date items. The <code>DATE_DIALOG_ID</code> is a static integer that uniquely identifies the {@link
+android.app.Dialog} that will display the date picker.</p>
+ </li>
- @Override
+ <li>Now add the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+method:
+<pre>
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
@@ -68,43 +73,30 @@
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
- // display the current date
+ // display the current date (this method is below)
updateDisplay();
}
</pre>
-<p class="note"><strong>Tip:</strong> Press Ctrl(or Cmd) + Shift + O to import all needed packages.</p>
- <p>We start by instantiating variables for our Views and date fields.
- The <code>DATE_DIALOG_ID</code> is a static integer that uniquely identifies the Dialog. In the
- <code>onCreate()</code> method, we get prepared by setting the layout and capturing the View elements.
- Then we create an on-click listener for the Button, so that when it is clicked it will
- show our DatePicker dialog. The <code>showDialog()</code> method will pop-up the date picker dialog
- by calling the <code>onCreateDialog()</code> callback method
- (which we'll define in the next section). We then create an
- instance of {@link java.util.Calendar} and get the current year, month and day. Finally, we call
- <code>updateDisplay()</code>—our own method (defined later) that will fill the TextView.</p>
+
+<p>First, the content is set to the <code>main.xml</code> layout. Then the {@link
+android.widget.TextView} and {@link android.widget.Button} elements are captured from the layout
+with {@link android.app.Activity#findViewById(int)}. A
+new {@link android.view.View.OnClickListener} is created for the
+{@link android.widget.Button}, so that when it is clicked, it
+will call {@link android.app.Activity#showDialog(int)}, passing the unique integer ID for
+the date picker dialog. Using {@link android.app.Activity#showDialog(int)} allows the {@link
+android.app.Activity} to manage the life-cycle of the dialog and will call the {@link
+android.app.Activity#onCreateDialog(int)} callback method to request the {@link android.app.Dialog}
+that should be displayed (which you'll
+define later). After the on-click listener is set, a new {@link java.util.Calendar} is created
+and the current year, month and day are acquired. Finally, the private
+<code>updateDisplay()</code> method is called in order to fill the {@link android.widget.TextView}
+with the current date.</p>
</li>
-<li>After the <code>onCreate()</code> method, add the <code>onCreateDialog()</code> callback method
-(which is called by <code>showDialog()</code>)
+<li>Add the <code>updateDisplay()</code> method:
<pre>
-@Override
-protected Dialog onCreateDialog(int id) {
- switch (id) {
- case DATE_DIALOG_ID:
- return new DatePickerDialog(this,
- mDateSetListener,
- mYear, mMonth, mDay);
- }
- return null;
-}
-</pre>
- <p>This method is passed the identifier we gave <code>showDialog()</code> and initializes
- the DatePicker to the date we retrieved from our Calendar instance.</p>
-</li>
-
-<li>Following that, add the <code>updateDisplay()</code> method:
-<pre>
- // updates the date we display in the TextView
+ // updates the date in the TextView
private void updateDisplay() {
mDateDisplay.setText(
new StringBuilder()
@@ -114,9 +106,13 @@
.append(mYear).append(" "));
}
</pre>
-<p>This uses the member date values to write the date to our TextView.</p>
+<p>This method uses the member date values declared for the class to write the date to the layout's
+{@link android.widget.TextView}, {@code mDateDisplay}, which was also declared and initialized
+above.</p>
</li>
-<li>Finally, add a listener that will be called when the user sets a new date:
+
+<li>Initialize a new {@link android.app.DatePickerDialog.OnDateSetListener} as a member of the
+<code>HelloDatePicker</code> class:
<pre>
// the callback received when the user "sets" the date in the dialog
private DatePickerDialog.OnDateSetListener mDateSetListener =
@@ -131,19 +127,45 @@
}
};
</pre>
- <p>This <code>OnDateSetListener</code> method listens for when the user is done setting the date
- (clicks the "Set" button). At that time, this fires and we update our member fields with
- the new date defined by the user and update our TextView by calling <code>updateDisplay()</code>.</p>
+<p>The {@link android.app.DatePickerDialog.OnDateSetListener} listens for when the user
+has set the date (by clicking the "Set" button). At that time, the {@link
+android.app.DatePickerDialog.OnDateSetListener#onDateSet(DatePicker,int,int,int) onDateSet()}
+callback method is called, which is defined to update the {@code mYear}, {@code mMonth}, and
+{@code mDay} member fields with the new date then call the private <code>updateDisplay()</code>
+method to update the {@link android.widget.TextView}.</p>
</li>
-<li>Now run it.</li>
+<li>Now add the {@link android.app.Activity#onCreateDialog(int)} callback method to the {@code
+HelloDatePicker} class:
+<pre>
+@Override
+protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case DATE_DIALOG_ID:
+ return new DatePickerDialog(this,
+ mDateSetListener,
+ mYear, mMonth, mDay);
+ }
+ return null;
+}
+</pre>
+<p>This is an {@link android.app.Activity} callback method that is passed the integer ID given to
+{@link android.app.Activity#showDialog(int)} (which is called by the button's {@link
+android.view.View.OnClickListener}). When the ID matches the switch case defined here, a {@link
+android.app.DatePickerDialog} is instantiated with the {@link
+android.app.DatePickerDialog.OnDateSetListener} created in the previous
+step, along with the date variables to initialize the widget date.</p>
+</li>
+
+<li>Run the application.</li>
</ol>
<p>When you press the "Change the date" button, you should see the following:</p>
<img src="images/hello-datepicker.png" width="150px" />
<h3>References</h3>
<ul>
-<li>{@link android.widget.DatePicker}</li>
+<li>{@link android.app.DatePickerDialog}</li>
+<li>{@link android.app.DatePickerDialog.OnDateSetListener}</li>
<li>{@link android.widget.Button}</li>
<li>{@link android.widget.TextView}</li>
<li>{@link java.util.Calendar}</li>
diff --git a/docs/html/resources/tutorials/views/hello-formstuff.jd b/docs/html/resources/tutorials/views/hello-formstuff.jd
index da4289c..3dd5f21 100644
--- a/docs/html/resources/tutorials/views/hello-formstuff.jd
+++ b/docs/html/resources/tutorials/views/hello-formstuff.jd
@@ -1,58 +1,99 @@
-page.title=Hello, Form Stuff
+page.title=Form Stuff
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>This page introduces a variety of widgets, like image buttons,
-text fields, checkboxes and radio buttons.</p>
+<p>This tutorial introduces a variety of widgets that are useful when creating forms, such as
+image buttons, text fields, checkboxes and radio buttons.</p>
<ol>
- <li>Start a new project/Activity called HelloFormStuff.</li>
- <li>Your layout file should have a basic LinearLayout:
- <pre>
+ <li>Start a new project named <em>HelloFormStuff</em>.</li>
+ <li>Your <code>res/layout/main.xml</code> file should already have a basic {@link
+android.widget.LinearLayout}:
+<pre>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
-
</LinearLayout>
</pre>
- <p>For each widget you want to add, just put the respective View inside here.</p>
-</li>
+ <p>For each widget you want to add, just put the respective View inside this {@link
+android.widget.LinearLayout}.</p>
+ </li>
</ol>
-<p class="note"><strong>Tip:</strong> As you add new Android code, press Ctrl(or Cmd) + Shift + O
-to import all needed packages.</p>
-
-
-<h2>ImageButton</h2>
-<p>A button with a custom image on it.
-We'll make it display a message when pressed.</p>
-<ol>
- <li><img src="images/android.png" align="right"/>
- Drag the Android image on the right (or your own image) into the
- res/drawable/ directory of your project.
- We'll use this for the button.</li>
- <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.ImageButton} element:
+<p>Each section below also assumes that your <code>HelloFormStuff</code> Activity has the following
+default implementation of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
<pre>
-<ImageButton
- android:id="@+id/android_button"
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:src="@drawable/android" />
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+}
</pre>
- <p>The source of the button
- is from the res/drawable/ directory, where we've placed the android.png.</p>
- <p class="note"><strong>Tip:</strong> You can also reference some of the many built-in
- images from the Android {@link android.R.drawable} resources,
- like <code>ic_media_play</code>, for a "play" button image. To do so, change the source
- attribute to <code>android:src="@android:drawable/ic_media_play"</code>.</p>
-</li>
-<li>To make the button to actually do something, add the following
-code at the end of the <code>onCreate()</code> method:
+
+
+
+<h2>Custom Button</h2>
+
+<p>In this section, you will create a button with a custom image instead of text, using the {@link
+android.widget.Button} widget and an XML file that defines three different images to use for the
+different button states. When the button is pressed, a short message will be displayed.</p>
+
+<img src="images/android_pressed.png" style="float:right;" title="android_pressed.png"/>
+<img src="images/android_focused.png" style="float:right;clear:right;" title="android_focused.png"/>
+<img src="images/android_normal.png" style="float:right;clear:right;" title="android_normal.png"/>
+<ol>
+ <li>Copy the images on the right into the <code>res/drawable/</code> directory of
+your project. These will be used for the different button states.</li>
+ <li>Create a new file in the <code>res/drawable/</code> directory named
+<code>android_button.xml</code>.
+Insert the following XML:
<pre>
-final ImageButton button = (ImageButton) findViewById(R.id.android_button);
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/android_pressed"
+ android:state_pressed="true" />
+ <item android:drawable="@drawable/android_focused"
+ android:state_focused="true" />
+ <item android:drawable="@drawable/android_normal" />
+</selector>
+</pre>
+ <p>This defines a single drawable resource, which will change its image based on the current
+state of the button. The first <code><item></code> defines
+<code>android_pressed.png</code> as the image when the button is pressed (it's been
+activated); the second <code><item></code> defines <code>android_focused.png</code> as the image
+when the button is focused (when the button is highlighted using the trackball or directional
+pad); and the third <code><item></code> defines <code>android_normal.png</code> as the image
+for the normal state (when neither pressed nor focused). This XML file now represents a single
+drawable resource and when referenced by a {@link android.widget.Button} for its background,
+the image displayed will change based on these three states.</p>
+ <p class="note"><strong>Note:</strong> The order of the <code><item></code> elements is
+important. When this drawable is referenced, the <code><item></code>s are traversed in-order to
+determine which one is appropriate for the current button state. Because the "normal" image is last,
+it is only applied when the conditions <code>android:state_pressed</code> and
+<code>android:state_focused</code> have both evaluated false.</li>
+ <li>Open the <code>res/layout/main.xml</code> file and add the {@link
+android.widget.Button} element:
+<pre>
+ <Button
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="10dp"
+ android:background="@drawable/android_button" />
+</pre>
+ <p>The <code>android:background</code> attribute specifies the drawable resource to use for the
+button background (which, when saved at <code>res/drawable/android.xml</code>, is
+referenced as <code>@drawable/android</code>). This replaces the normal background image
+used for buttons throughout the system. In order for the drawable to change its image based on
+the button state, the image must be applied to the background.</p>
+</li>
+
+<li>To make the button do something when pressed, add the following
+code at the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
+<pre>
+final Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Perform action on clicks
@@ -60,34 +101,41 @@
}
});
</pre>
-<p>This captures our ImageButton from the layout, then adds an on-click listener to it.
-The {@link android.view.View.OnClickListener} must define the <code>onClick()</code> method, which
-defines the action to be made when the button is clicked. Here, we show a
-{@link android.widget.Toast} message when clicked.</p>
+<p>This captures the {@link android.widget.Button} from the layout, then adds an {@link
+android.view.View.OnClickListener}. The {@link android.view.View.OnClickListener}
+must implement the {@link android.view.View.OnClickListener#onClick(View)} callback method, which
+defines the action to be made when the button is clicked. In this example, a
+{@link android.widget.Toast} message will be displayed.</p>
</li>
-<li>Run it.</li>
+<li>Now run the application.</li>
</ol>
<h2>EditText</h2>
-<p>A text field for user input. We'll make it display the text entered so far when the "Enter" key is pressed.</p>
+
+<p>In this section, you will create a text field for user input, using the {@link
+android.widget.EditText} widget. Once text has been entered into the field, the "Enter" key will
+display the text in a toast message.</p>
<ol>
- <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.EditText} element:
+ <li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.EditText}
+element (inside the {@link android.widget.LinearLayout}):
<pre>
-<EditText
- android:id="@+id/edittext"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
+ <EditText
+ android:id="@+id/edittext"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
</pre>
</li>
-<li>To do something with the text that the user enters, add the following code
-to the end of the <code>onCreate()</code> method:
+<li>To do something with the text that the user types, add the following code
+to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
final EditText edittext = (EditText) findViewById(R.id.edittext);
edittext.setOnKeyListener(new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
- if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
+ // If the event is a key-down event on the "enter" button
+ if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
+ (keyCode == KeyEvent.KEYCODE_ENTER)) {
// Perform action on key press
Toast.makeText(HelloFormStuff.this, edittext.getText(), Toast.LENGTH_SHORT).show();
return true;
@@ -96,37 +144,44 @@
}
});
</pre>
-<p>This captures our EditText element from the layout, then adds an on-key listener to it.
-The {@link android.view.View.OnKeyListener} must define the <code>onKey()</code> method, which
-defines the action to be made when a key is pressed. In this case, we want to listen for the
-Enter key (when pressed down), then pop up a {@link android.widget.Toast} message with the
-text from the EditText field. Be sure to return <var>true</var> after the event is handled,
-so that the event doesn't bubble-up and get handled by the View (which would result in a
-carriage return in the text field).</p>
-<li>Run it.</li>
+<p>This captures the {@link android.widget.EditText} element from the layout and adds an {@link
+android.view.View.OnKeyListener}. The {@link android.view.View.OnKeyListener} must implement the
+{@link android.view.View.OnKeyListener#onKey(View,int,KeyEvent)} method, which
+defines the action to be made when a key is pressed while the widget has focus. In this case, the
+method is defined to listen for the Enter key (when pressed down), then pop up a {@link
+android.widget.Toast} message with the text that has been entered. The {@link
+android.view.View.OnKeyListener#onKey(View,int,KeyEvent)} method should always return
+<code>true</code> if the event has been handled, so that the event doesn't bubble-up (which would
+result in a carriage return in the text field).</p>
+</li>
+<li>Run the application.</li>
</ol>
<h2>CheckBox</h2>
-<p>A checkbox for selecting items. We'll make it display the the current state when pressed.</p>
+
+<p>In this section, you will create a checkbox for selecting items, using the {@link
+android.widget.CheckBox} widget. When the checkbox is pressed, a toast message will
+indicate the current state of the checkbox.</p>
<ol>
- <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.CheckBox} element:
+ <li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.CheckBox}
+element (inside the {@link android.widget.LinearLayout}):
<pre>
-<CheckBox android:id="@+id/checkbox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="check it out" />
+ <CheckBox android:id="@+id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="check it out" />
</pre>
</li>
<li>To do something when the state is changed, add the following code
-to the end of the <code>onCreate()</code> method:
+to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
final CheckBox checkbox = (CheckBox) findViewById(R.id.checkbox);
checkbox.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
- // Perform action on clicks
- if (checkbox.isChecked()) {
+ // Perform action on clicks, depending on whether it's now checked
+ if (((CheckBox) v).isChecked()) {
Toast.makeText(HelloFormStuff.this, "Selected", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(HelloFormStuff.this, "Not selected", Toast.LENGTH_SHORT).show();
@@ -134,52 +189,62 @@
}
});
</pre>
-<p>This captures our CheckBox element from the layout, then adds an on-click listener to it.
-The {@link android.view.View.OnClickListener} must define the <code>onClick()</code> method, which
-defines the action to be made when the checkbox is clicked. Here, we query the current state of the
-checkbox, then pop up a {@link android.widget.Toast} message that displays the current state.
-Notice that the CheckBox handles its own state change between checked and un-checked, so we just
-ask which it currently is.</p>
+<p>This captures the {@link android.widget.CheckBox} element from the layout, then adds an {@link
+android.view.View.OnClickListener}. The {@link android.view.View.OnClickListener} must implement the
+{@link android.view.View.OnClickListener#onClick(View)} callback method, which
+defines the action to be made when the checkbox is clicked. When clicked, {@link
+android.widget.CompoundButton#isChecked()} is called to check the new state of the check box. If it
+has been checked, then a {@link android.widget.Toast} displays the message "Selected", otherwise it
+displays "Not selected". Note that the {@link android.view.View} object that is passed in the {@link
+android.view.View.OnClickListener#onClick(View)} callback must be cast to a {@link
+android.widget.CheckBox} because the {@link android.widget.CompoundButton#isChecked()} method is
+not defined by the parent {@link android.view.View} class. The {@link android.widget.CheckBox}
+handles its own state changes, so you only need to query the current state.</p>
+</li>
<li>Run it.</li>
</ol>
-<p class="note"><strong>Tip:</strong> If you find that you need to change the state
-in another way (such as when loading a saved {@link android.preference.CheckBoxPreference}),
-use <code>setChecked(true)</code> or <code>toggle()</code>.</p>
+<p class="note"><strong>Tip:</strong> If you need to change the state
+yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}),
+use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link
+android.widget.CompoundButton#toggle()} method.</p>
<h2>RadioButton</h2>
-<p>Two mutually-exclusive radio buttons—enabling one disables the other.
-When each is pressed, we'll pop up a message.</p>
+
+<p>In this section, you will create two mutually-exclusive radio buttons (enabling one disables
+the other), using the {@link android.widget.RadioGroup} and {@link android.widget.RadioButton}
+widgets. When either radio button is pressed, a toast message will be displayed.</p>
<ol>
- <li>Open the layout file and, inside the LinearLayout, add two {@link android.widget.RadioButton}s,
-inside a {@link android.widget.RadioGroup}:
+ <li>Open the <code>res/layout/main.xml</code> file and add two {@link
+android.widget.RadioButton}s, nested in a {@link android.widget.RadioGroup} (inside the {@link
+android.widget.LinearLayout}):
<pre>
-<RadioGroup
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <RadioButton android:id="@+id/radio_red"
- android:layout_width="wrap_content"
+ <RadioGroup
+ android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:text="Red" />
-
- <RadioButton android:id="@+id/radio_blue"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Blue" />
-
-</RadioGroup>
+ android:orientation="vertical">
+ <RadioButton android:id="@+id/radio_red"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Red" />
+ <RadioButton android:id="@+id/radio_blue"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Blue" />
+ </RadioGroup>
</pre>
+<p>It's important that the {@link android.widget.RadioButton}s are grouped together by the {@link
+android.widget.RadioGroup} element so that no more than one can be selected at a time. This logic
+is automatically handled by the Android system. When one {@link android.widget.RadioButton} within
+a group is selected, all others are automatically deselected.</p>
</li>
-<li>To do something when each is selected, we'll need an OnClickListener. Unlike the other
-listeners we've created, instead of creating this one as an anonymous inner class,
-we'll create it as a new object. This way, we can re-use the OnClickLIstener for
-both RadioButtons. So, add the following code in the HelloFormStuff Activity
-(<em>outside</em> the <code>onCreate()</code> method):
+
+<li>To do something when each {@link android.widget.RadioButton} is selected, you need an
+{@link android.view.View.OnClickListener}. In this case, you want the listener to be re-usable, so
+add the following code to create a new member in the <code>HelloFormStuff</code> Activity:
<pre>
-OnClickListener radio_listener = new OnClickListener() {
+private OnClickListener radio_listener = new OnClickListener() {
public void onClick(View v) {
// Perform action on clicks
RadioButton rb = (RadioButton) v;
@@ -187,68 +252,124 @@
}
};
</pre>
-<p>Our <code>onClick()</code> method will be handed the View clicked, so the first thing to do
-is cast it into a RadioButton. Then we pop up a
-{@link android.widget.Toast} message that displays the selection.</p>
-<li>Now, at the bottom of the <code>onCreate()</code> method, add the following:
+<p>First, the {@link android.view.View} that is passed to the {@link
+android.view.View.OnClickListener#onClick(View)} method is cast into a RadioButton. Then a
+{@link android.widget.Toast} message displays the selected radio button's text.</p>
+<li>Now, at the bottom of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method, add
+the following:
<pre>
final RadioButton radio_red = (RadioButton) findViewById(R.id.radio_red);
final RadioButton radio_blue = (RadioButton) findViewById(R.id.radio_blue);
radio_red.setOnClickListener(radio_listener);
radio_blue.setOnClickListener(radio_listener);
</pre>
-<p>This captures each of the RadioButtons from our layout and adds the newly-created
-OnClickListener to each.</p>
-<li>Run it.</li>
+<p>This captures each of the {@link android.widget.RadioButton}s from the layout and adds the
+newly-created {@link android.view.View.OnClickListener} to each.</p>
+<li>Run the application.</li>
</ol>
-<p class="note"><strong>Tip:</strong> If you find that you need to change the state of a
-RadioButton in another way (such as when loading a saved {@link android.preference.CheckBoxPreference}),
-use <code>setChecked(true)</code> or <code>toggle()</code>.</p>
+
+<p class="note"><strong>Tip:</strong> If you need to change the state
+yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}),
+use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link
+android.widget.CompoundButton#toggle()} method.</p>
<h2>ToggleButton</h2>
-<p>A button used specifically for toggling something on and off.</p>
+
+<p>In this section, you'll create a button used specifically for toggling between two
+states, using the {@link android.widget.ToggleButton} widget. This widget is an excellent
+alternative to radio buttons if you have two simple states that are mutually exclusive ("on" and
+"off", for example).</p>
<ol>
- <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.ToggleButton} element:
+ <li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.ToggleButton}
+element (inside the {@link android.widget.LinearLayout}):
<pre>
-<ToggleButton android:id="@+id/togglebutton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
+ <ToggleButton android:id="@+id/togglebutton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textOn="Vibrate on"
+ android:textOff="Vibrate off"/>
</pre>
+ <p>The attributes <code>android:textOn</code> and <code>android:textOff</code> specify the text
+for the button when the button has been toggled on or off. The default values are "ON" and
+"OFF".</p>
</li>
<li>To do something when the state is changed, add the following code
-to the end of the <code>onCreate()</code> method:
+to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
final ToggleButton togglebutton = (ToggleButton) findViewById(R.id.togglebutton);
togglebutton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Perform action on clicks
if (togglebutton.isChecked()) {
- Toast.makeText(HelloFormStuff.this, "ON", Toast.LENGTH_SHORT).show();
+ Toast.makeText(HelloFormStuff.this, "Checked", Toast.LENGTH_SHORT).show();
} else {
- Toast.makeText(HelloFormStuff.this, "OFF", Toast.LENGTH_SHORT).show();
+ Toast.makeText(HelloFormStuff.this, "Not checked", Toast.LENGTH_SHORT).show();
}
}
});
</pre>
-<p>This captures our ToggleButton element from the layout, then adds an on-click listener to it.
-The {@link android.view.View.OnClickListener} must define the <code>onClick()</code> method, which
-defines the action to be made when the button is clicked. Here, we query the current state of the
-ToggleButton, then pop up a {@link android.widget.Toast} message that displays the current state.
-Notice that the ToggleButton handles its own state change between checked and un-checked, so we just
-ask which it is.</p>
-<li>Run it.</li>
+<p>This captures the {@link android.widget.ToggleButton} element from the layout, then adds an
+{@link android.view.View.OnClickListener}. The {@link android.view.View.OnClickListener} must
+implement the {@link android.view.View.OnClickListener#onClick(View)} callback method, which
+defines the action to perform when the button is clicked. In this example, the callback
+method checks the new state of the button, then shows a {@link android.widget.Toast} message that
+indicates the current state.</p>
+
+<p>Notice that the {@link android.widget.ToggleButton} handles its own state change between checked
+and unchecked, so you just ask which it is.</p>
+<li>Run the application.</li>
</ol>
-<p class="note"><strong>Tip:</strong> By default, the text on the button is "ON" and "OFF", but
-you can change each of these with <code>setTextOn(<var>CharSequence</var>)</code> and
-<code>setTextOff(<var>CharSequence</var>)</code>. And, if you find that you need to change the state
-in another way (such as when loading a saved {@link android.preference.CheckBoxPreference}),
-use <code>setChecked(true)</code> or <code>toggle()</code>. </p>
+<p class="note"><strong>Tip:</strong> If you need to change the state
+yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}),
+use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link
+android.widget.CompoundButton#toggle()} method.</p>
-<p>If you've added all the form items above, your application should look something like this:</p>
+
+
+<h2>RatingBar</h2>
+
+<p>In this section, you'll create a widget that allows the user to provide a rating,
+with the {@link android.widget.RatingBar} widget.</p>
+
+<ol>
+ <li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.RatingBar}
+element (inside the {@link android.widget.LinearLayout}):
+<pre>
+ <RatingBar android:id="@+id/ratingbar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:numStars="5"
+ android:stepSize="1.0"/>
+</pre>
+ <p>The <code>android:numStars</code> attribute defines how many stars to display for the rating
+bar. The <code>android:stepSize</code> attribute defines the granularity for each
+star (for example, a value of <code>0.5</code> would allow half-star ratings). </p>
+</li>
+<li>To do something when a new rating has been set, add the following code
+to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
+<pre>
+final RatingBar ratingbar = (RatingBar) findViewById(R.id.ratingbar);
+ratingbar.setOnRatingBarChangeListener(new OnRatingBarChangeListener() {
+ public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {
+ Toast.makeText(HelloFormStuff.this, "New Rating: " + rating, Toast.LENGTH_SHORT).show();
+ }
+});
+</pre>
+<p>This captures the {@link android.widget.RatingBar} widget from the layout with {@link
+android.app.Activity#findViewById(int)} and then sets an {@link
+android.widget.RatingBar.OnRatingBarChangeListener}. The {@link
+android.widget.RatingBar.OnRatingBarChangeListener#onRatingChanged(RatingBar,float,boolean)
+onRatingChanged()} callback method then defines the action to perform when the user sets a rating.
+In this case, a simple {@link android.widget.Toast} message displays the new rating.</p>
+
+<li>Run the application.</li>
+</ol>
+
+<p>If you've added all the form widgets above, your application should look like this:</p>
<img src="images/hello-formstuff.png" width="150px" />
<h3>References</h3>
@@ -258,5 +379,6 @@
<li>{@link android.widget.CheckBox}</li>
<li>{@link android.widget.RadioButton}</li>
<li>{@link android.widget.ToggleButton}</li>
+ <li>{@link android.widget.RatingBar}</li>
</ul>
diff --git a/docs/html/resources/tutorials/views/hello-gallery.jd b/docs/html/resources/tutorials/views/hello-gallery.jd
index 084f912..12d5a91 100644
--- a/docs/html/resources/tutorials/views/hello-gallery.jd
+++ b/docs/html/resources/tutorials/views/hello-gallery.jd
@@ -1,16 +1,21 @@
-page.title=Hello, Gallery
+page.title=Gallery
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>A {@link android.widget.Gallery} is a View commonly used to display items in a horizontally scrolling list
-that locks the current selection at the center. When one is selected, we'll show a message.</p>
+<p>{@link android.widget.Gallery} is a layout widget used to display items in a
+horizontally scrolling list and positions the current selection at the center of the view.</p>
+
+<p>In this tutorial, you'll create a gallery of photos and then display a toast message each time a
+gallery item is selected.</p>
<ol>
- <li>Start a new project/Activity called HelloGallery.</li>
- <li>Add some images to your res/drawable/ directory.</li>
- <li>Open the layout file and make it like so:
+ <li>Start a new project named <em>HelloGallery</em>.</li>
+ <li>Find some photos you'd like to use, or use these <a
+href="{@docRoot}shareables/sample_images.zip">sample images</a>. Save the images into the project's
+<code>res/drawable/</code> directory.</li>
+ <li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
<?xml version="1.0" encoding="utf-8"?>
<Gallery xmlns:android="http://schemas.android.com/apk/res/android"
@@ -19,10 +24,10 @@
android:layout_height="wrap_content"
/>
</pre>
-</li>
+ </li>
-
-<li>Open the HelloGallery.java file. Insert the following for the <code>onCreate()</code> method:
+<li>Open the <code>HelloGallery.java</code> file and insert the following code for the
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -39,19 +44,46 @@
});
}
</pre>
- <p>We start as usual: set the layout and capture the View we want (our Gallery).
-We then set an Adapter, called ImageAdapter for the Gallery—this is a new class that
-we'll create next. Then we create an item click listener for the Gallery. This is like a normal
-on-click listener (which you might be familiar with for buttons), but it listens to each item
-that we've added to the Gallery. The <code>onItemClick()</code> callback method
-receives the AdapterView where the click occurred, the specific View that received the click, the
-position of the View clicked (zero-based), and the row id of the item clicked (if applicable). All
-that we care about is the position, so that we can pop up a {@link android.widget.Toast} message that
-tells us the index position of the item clicked. We do this with <code>Toast.makeText().show()</code>.
+ <p>This starts by setting the {@code main.xml} layout as the content view and then capturing the
+{@link android.widget.Gallery} from
+the layout with {@link
+android.app.Activity#findViewById(int)}. A custom {@link android.widget.BaseAdapter} called
+<code>ImageAdapter</code> is
+instantiated and applied to the {@link android.widget.Gallery} with {@link
+android.widget.AdapterView#setAdapter(T) setAdapter()}. (The <code>ImageAdapter</code> class is
+defined next.)
+Then an anonymous {@link android.widget.AdapterView.OnItemClickListener} is instantiated. The
+{@link android.widget.AdapterView.OnItemClickListener#onItemClick(AdapterView,View,int,long)}
+callback method receives the {@link android.widget.AdapterView} where the click occurred, the
+specific {@link android.view.View} that received the click, the
+position of the {@link android.view.View} clicked (zero-based), and the row ID of the item clicked
+(if applicable). In this example, all that's needed is the position of the click to show a {@link
+android.widget.Toast} message that says the position of the item, using
+{@link android.widget.Toast#makeText(Context,CharSequence,int)} and {@link
+android.widget.Toast#show()} (in a real world scenario, this ID could be used to get the full sized
+image for some other task).
</p>
</li>
+ <li>Create a new XML file in the <code>res/values/</code> directory named <code>attrs.xml</code>.
+Insert the following:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="HelloGallery">
+ <attr name="android:galleryItemBackground" />
+ </declare-styleable>
+</resources>
+</pre>
+ <p>This is a custom styleable resource that can be applied to a layout. In this case, it will be
+applied to the individual items placed into the {@link android.widget.Gallery} widget. The
+<code><attr></code> element defines a specific attribute for the styleable, and in this case, it
+refers to an existing platform attribute, {@link android.R.attr#galleryItemBackground}, which
+defines a border styling for gallery items. In the next step, you'll
+see how this attribute is referenced and then later applied to each item in the gallery.</p>
+ </li>
-<li>After the <code>onCreate()</code> method, add the <code>ImageAdapter</code> class:
+ <li>Go back to the <code>HelloGallery.java</code> file. After the {@link
+ android.app.Activity#onCreate(Bundle)} method, define the custom <code>ImageAdapter</code> class:
<pre>
public class ImageAdapter extends BaseAdapter {
int mGalleryItemBackground;
@@ -100,26 +132,30 @@
}
</pre>
<p>First, there are a few member variables, including an array of IDs that reference
-the images we placed in our drawable resources directory.</p>
-<p>Next is the constructor, where we define the member Context. The rest of the constructor
-sets up a reference for our Gallery them, which adds the nice framing for each Gallery item.
-Once we have our <code>mGalleryItemBackground</code>, it's important to recycle the
-StyledAttribute for later re-use.</p>
-<p>The next three methods are required for basic member queries.
-But then we have the <code>getView()</code> method, which is called
-for each item read by our ImageAdapter, when the Gallery is being built. Here, we
-use our member Context to create a new {@link android.widget.ImageView}. We then define
-the image resource with the current position of the Gallery items (corresponding to our
-array of drawables), set the dimensions for the ImageView,
-set the image scaling to fit the ImageView dimensions, then finally set the
-background theme for the ImageView.</p>
+the images saved in the drawable resources directory ({@code res/drawable/}).</p>
+<p>Next is the class constructor, where the {@link android.content.Context} for an {@code
+ImageAdapter} instance is defined and the styleable
+resource defined in the last step is acquired and saved to a local field. At the end of the
+constructor, {@link android.content.res.TypedArray#recycle()} is called on the {@link
+android.content.res.TypedArray} so it can be re-used by the system.</p>
+<p>The methods {@link android.widget.Adapter#getCount()}, {@link
+android.widget.Adapter#getItem(int)}, and {@link android.widget.Adapter#getItemId(int)} are methods
+that must be implemented for simple queries on the {@link android.widget.Adapter}.
+The {@link android.widget.Adapter#getView(int,View,ViewGroup) method does the
+work to apply an image to an {@link android.widget.ImageView} that will be embedded in the
+{@link android.widget.Gallery}. In this method, the member {@link android.content.Context} is used
+to create a new {@link android.widget.ImageView}. The {@link android.widget.ImageView} is prepared
+by applying an image from the local array of drawable resources, setting the {@link
+android.widget.Gallery.LayoutParams} height and width for the image, setting the scale to fit the
+{@link android.widget.ImageView} dimensions, and then finally setting the background to use the
+styleable attribute acquired in the constructor.</p>
-<p>See {@link android.widget.ImageView.ScaleType}
-for other image scaling options, in case you want to avoid stretching images that don't
-exactly match the ImageView dimensions.</p>
+<p>See {@link android.widget.ImageView.ScaleType} for other image scaling options.</p>
+</li>
-<li>Now run it.</li>
+<li>Run the application.</li>
</ol>
+
<p>You should see something like this:</p>
<img src="images/hello-gallery.png" width="150px" />
@@ -129,7 +165,7 @@
<li>{@link android.widget.BaseAdapter}</li>
<li>{@link android.widget.Gallery}</li>
<li>{@link android.widget.ImageView}</li>
- <li>{@link android.widget.Toast}</li>
+ <li>{@link android.widget.AdapterView.OnItemClickListener}</li>
</ul>
diff --git a/docs/html/resources/tutorials/views/hello-gridview.jd b/docs/html/resources/tutorials/views/hello-gridview.jd
index ffb6c93..03bfc54 100644
--- a/docs/html/resources/tutorials/views/hello-gridview.jd
+++ b/docs/html/resources/tutorials/views/hello-gridview.jd
@@ -1,33 +1,44 @@
-page.title=Hello, GridView
+page.title=Grid View
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>A {@link android.widget.GridView} displays items in a two-dimensional, scrolling grid. The items
-are acquired from a {@link android.widget.ListAdapter}.</p>
+<p>{@link android.widget.GridView} is a {@link android.view.ViewGroup} that displays items in a
+two-dimensional,
+scrollable grid. The grid items are automatically inserted to the layout using a {@link
+android.widget.ListAdapter}.</p>
+
+<p>In this tutorial, you'll create a grid of image thumbnails. When an item is selected, a
+toast message will display the position of the image.</p>
<ol>
- <li>Start a new project/Activity called HelloGridView.</li>
- <li>Find some photos you'd like to use, or copy some from the SDK samples res/drawable/
- folder of your project.</li>
- <li>Open the layout and make it like so:
+ <li>Start a new project named <em>HelloGridView</em>.</li>
+ <li>Find some photos you'd like to use, or <a
+href="{@docRoot}shareables/sample_images.zip">download these sample images</a>. Save the image files
+into the project's
+<code>res/drawable/</code> directory.</li>
+ <li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
+ android:columnWidth="90dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
- android:columnWidth="90dp"
android:stretchMode="columnWidth"
android:gravity="center"
/>
</pre>
+ <p>This {@link android.widget.GridView} will fill the entire screen. The attributes are rather
+self explanatory. For more information about valid attributes, see the {@link
+android.widget.GridView} reference.</p>
</li>
- <li>Open the HelloGridView Java file. Insert the following for the <code>onCreate()</code> method:
+ <li>Open <code>HelloGridView.java</code> and insert the following code for the
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -35,12 +46,33 @@
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this));
+
+ gridview.setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
+ Toast.makeText(HelloGridView.this, "" + position, Toast.LENGTH_SHORT).show();
+ }
+ });
}
</pre>
- <p>Here, we get a handle on our GridView, from the layout, and give it an Adapter.
- We're actually going to create our own Adapter called ImageAdapter.</p>
+ <p>After the {@code main.xml} layout is set for the content view, the
+{@link android.widget.GridView} is captured from the layout with {@link
+android.app.Activity#findViewById(int)}. The {@link
+android.widget.GridView#setAdapter(T) setAdapter()} method then sets a custom adapter ({@code
+ImageAdapter}) as the source for all items to be displayed in the grid. The {@code ImageAdapter} is
+created in the next step.</p>
+<p>To do something when an item in the grid is clicked, the {@link
+android.widget.AdapterView#setOnItemClickListener(OnItemClickListener) setOnItemClickListener()}
+method is passed a new {@link android.widget.AdapterView.OnItemClickListener}. This anonymous
+instance defines the {@link
+android.widget.AdapterView.OnItemClickListener#onItemClick(AdapterView,View,int,long)
+onItemClick()} callback method to show a {@link android.widget.Toast} that displays the index
+position (zero-based) of the selected item (in a real world scenario, the position could be used to
+get the full sized
+image for some other task).</p>
+
</li>
-<li>Create a new class (nested or otherwise), called ImageAdapter, which extends {@link android.widget.BaseAdapter}:
+<li>Create a new class called <code>ImageAdapter</code> that extends {@link
+android.widget.BaseAdapter}:
<pre>
public class ImageAdapter extends BaseAdapter {
private Context mContext;
@@ -93,37 +125,58 @@
};
}
</pre>
- <p>First we take care of some required methods inherited from BaseAdapter.
- The constructor and <code>getCount()</code> are self-explanitory. Normally, <code>getItem()</code>
- should return the actual object at the specified position in our Adapter, but for this Hello World,
- we're not going to bother. Likewise, <code>getItemId()</code> should return the row id of
- the item, but right now we don't care.</p>
- <p>However, <code>getView()</code> is the method we care about. This one creates a new View for each image that we
- put in our ImageAdapter. So we're going to create an ImageView each time. When this is called, we're
- going to receive a View, which is likely a recycled View object (at least after the first call), so we
- check for this—if it's null, we initialize the ImageView and setup all the properties we want.
- The <code>LayoutParams()</code> initialization sets the height and width of the View—this ensures
- that no matter the drawable size, each image is resized and cropped to fit in the ImageView (if necessary).
- With <code>setScaleType()</code>, we say that images should be cropped toward the center (if necessary).
- And finally, we set the padding within the ImageView. (Note that, if the images have various aspect-ratios,
- as they do in this demo, then less padding will cause for more cropping of the image, if it does not match
- the dimensions given to the ImageView.) At the end of <code>getView()</code> we set the image resource and
- return the ImageView.</p>
- <p>All that's left is our array or drawable resources at the bottom.</p>
+<p>First, this implements some required methods inherited from {@link
+android.widget.BaseAdapter}. The constructor and {@link
+android.widget.Adapter#getCount()} are self-explanatory. Normally, {@link
+android.widget.Adapter#getItem(int)} should return the actual object at the specified position in
+the adapter, but it's ignored for this example. Likewise, {@link
+android.widget.Adapter#getItemId(int)} should return the row id of the item, but it's not
+needed here.</p>
+
+<p>The first method necessary is {@link android.widget.Adapter#getView(int,View,ViewGroup)
+getView()}. This method creates a new {@link android.view.View} for each image added to the {@code
+ImageAdapter}. When this is called, a {@link android.view.View} is passed in, which is normally a
+recycled object (at least after this has been called once), so there's a check to see if the
+object is null. If it <em>is</em> null, an {@link android.widget.ImageView} is instantiated and
+configured with desired properties for the image presentation:</p>
+<ul>
+ <li>{@link android.view.View#setLayoutParams(ViewGroup.LayoutParams)} sets
+the height and width for the View—this ensures that, no matter the size of the drawable, each
+image is resized and cropped to fit in these dimensions, as appropriate.</li>
+ <li>{@link android.widget.ImageView#setScaleType(ImageView.ScaleType)} declares that images should
+be cropped toward the center (if necessary).</li>
+ <li>{@link android.widget.ImageView#setPadding(int,int,int,int)} defines the padding for all
+sides. (Note that, if the images have different aspect-ratios, then less
+padding will cause for more cropping of the image if it does not match
+the dimensions given to the ImageView.)</li>
+</ul>
+
+<p>If the {@link android.view.View} passed to {@link
+android.widget.Adapter#getView(int,View,ViewGroup) getView()} is <em>not</em> null, then the local
+{@link android.widget.ImageView} is initialized with the recycled {@link android.view.View}
+object.</p>
+
+<p>At the end of the {@link android.widget.Adapter#getView(int,View,ViewGroup) getView()} method,
+the {@code
+position} integer passed into the method is used to select an image from the {@code mThumbIds}
+array, which is set as the image resource for the {@link android.widget.ImageView}.</p>
+<p>All that's left is to define the {@code mThumbIds} array of drawable resources.</p>
</li>
-<li>Run it.</li>
+<li>Run the application.</li>
</ol>
<p>Your grid layout should look something like this:</p>
<img src="images/hello-gridview.png" width="150px" />
-<p>Try experimenting with the behaviors of the GridView and ImageView by adjusting their properties. For example,
- instead of setting the ImageView LayoutParams, you can try using
- {@link android.widget.ImageView#setAdjustViewBounds(boolean)}. </p>
+<p>Try experimenting with the behaviors of the {@link android.widget.GridView} and {@link
+android.widget.ImageView} elements by adjusting their properties. For example, instead of using
+{@link android.view.View#setLayoutParams(ViewGroup.LayoutParams)}, try using
+{@link android.widget.ImageView#setAdjustViewBounds(boolean)}. </p>
<h3>References</h3>
<ul>
<li>{@link android.widget.GridView}</li>
<li>{@link android.widget.ImageView}</li>
<li>{@link android.widget.BaseAdapter}</li>
+ <li>{@link android.widget.AdapterView.OnItemClickListener}</li>
</ul>
diff --git a/docs/html/resources/tutorials/views/hello-linearlayout.jd b/docs/html/resources/tutorials/views/hello-linearlayout.jd
index 0e8947c..8e072b1 100644
--- a/docs/html/resources/tutorials/views/hello-linearlayout.jd
+++ b/docs/html/resources/tutorials/views/hello-linearlayout.jd
@@ -1,16 +1,18 @@
-page.title=Hello, LinearLayout
+page.title=Linear Layout
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>A {@link android.widget.LinearLayout} is a GroupView that will lay child View elements
-vertically or horizontally.</p>
+<p>{@link android.widget.LinearLayout} is a {@link android.view.ViewGroup} that displays child
+{@link android.view.View} elements in a linear direction, either vertically or horizontally.</p>
+<p>You should be careful about over-using the {@link android.widget.LinearLayout}. If you begin
+nesting multiple {@link android.widget.LinearLayout}s, you may want to consider using a {@link
+android.widget.RelativeLayout} instead.</p>
<ol>
- <li>Start a new project/Activity called HelloLinearLayout.</li>
- <li>Open the layout file.
- Make it like so:
+ <li>Start a new project named <em>HelloLinearLayout</em>.</li>
+ <li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -18,113 +20,114 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent">
- <LinearLayout
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_weight="1">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1">
+ <TextView
+ android:text="red"
+ android:gravity="center_horizontal"
+ android:background="#aa0000"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"/>
+ <TextView
+ android:text="green"
+ android:gravity="center_horizontal"
+ android:background="#00aa00"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"/>
+ <TextView
+ android:text="blue"
+ android:gravity="center_horizontal"
+ android:background="#0000aa"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"/>
+ <TextView
+ android:text="yellow"
+ android:gravity="center_horizontal"
+ android:background="#aaaa00"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"/>
+ </LinearLayout>
- <TextView
- android:text="red"
- android:gravity="center_horizontal"
- android:background="#aa0000"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_weight="1"/>
-
- <TextView
- android:text="green"
- android:gravity="center_horizontal"
- android:background="#00aa00"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_weight="1"/>
-
- <TextView
- android:text="blue"
- android:gravity="center_horizontal"
- android:background="#0000aa"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_weight="1"/>
-
- <TextView
- android:text="yellow"
- android:gravity="center_horizontal"
- android:background="#aaaa00"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_weight="1"/>
-
- </LinearLayout>
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_weight="1">
-
- <TextView
- android:text="row one"
- android:textSize="15pt"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
-
- <TextView
- android:text="row two"
- android:textSize="15pt"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
-
- <TextView
- android:text="row three"
- android:textSize="15pt"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
-
- <TextView
- android:text="row four"
- android:textSize="15pt"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
-
- </LinearLayout>
-
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1">
+ <TextView
+ android:text="row one"
+ android:textSize="15pt"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ <TextView
+ android:text="row two"
+ android:textSize="15pt"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ <TextView
+ android:text="row three"
+ android:textSize="15pt"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ <TextView
+ android:text="row four"
+ android:textSize="15pt"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ </LinearLayout>
+
</LinearLayout>
</pre>
- <p>Carefully inspect the XML. You'll notice how this layout works a lot like
- an HTML layout. There is one parent LinearLayout that is defined to lay
- its child elements vertically. The first child is another LinearLayout that uses a horizontal layout
- and the second uses a vertical layout. Each LinearLayout contains several {@link android.widget.TextView}
- elements.</p>
+
+<p>Carefully inspect this XML. There is a root {@link android.widget.LinearLayout} that defines
+its orientation to be vertical—all child {@link android.view.View}s (of which it has two) will
+be stacked vertically. The first child is
+another {@link android.widget.LinearLayout} that uses a horizontal orientation and the second child
+is a {@link android.widget.LinearLayout} that uses a vertical orientation. Each of these nested
+{@link android.widget.LinearLayout}s contain several {@link android.widget.TextView} elements, which
+are oriented with each other in the manner defined by their parent {@link
+android.widget.LinearLayout}.</p>
</li>
-<li>Now open the HelloLinearLayout Activity and be sure it loads this layout in the <code>onCreate()</code> method:</p>
+<li>Now open <code>HelloLinearLayout.java</code> and be sure it loads the
+<code>res/layout/main.xml</code> layout in the
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
</pre>
-<p><code>R.layout.main</code> refers to the <code>main.xml</code> layout file.</p>
+<p>The {@link android.app.Activity#setContentView(int)} method loads the
+layout file for the {@link android.app.Activity}, specified by the resource
+ID — <code>R.layout.main</code> refers to the <code>res/layout/main.xml</code> layout
+file.</p>
</li>
-<li>Run it.</li>
+<li>Run the application.</li>
</ol>
<p>You should see the following:</p>
<img src="images/hello-linearlayout.png" width="150px" />
-<p>Notice how the various XML attributes define the View's behavior.
-Pay attention to the effect of the <code>layout_weight</code>. Try
- experimenting with different values to see how the screen real estate is
- distributed based on the weight of each element.</p>
+<p>Notice how the XML attributes define each View's behavior. Try
+experimenting with different values for <code>android:layout_weight</code> to see how the screen
+real estate is distributed based on the weight of each element. See the <a
+href="{@docRoot}guide/topics/ui/layout-objects.html#linearlayout">Common Layout Objects</a>
+document for more about how {@link android.widget.LinearLayout} handles the
+<code>android:layout_weight</code> attribute.</p>
<h3>References</h3>
<ul>
<li>{@link android.widget.LinearLayout}</li>
-<li>{@link android.widget.TextView}</li>
+ <li>{@link android.widget.TextView}</li>
</ul>
diff --git a/docs/html/resources/tutorials/views/hello-listview.jd b/docs/html/resources/tutorials/views/hello-listview.jd
index d90005b..194258e 100644
--- a/docs/html/resources/tutorials/views/hello-listview.jd
+++ b/docs/html/resources/tutorials/views/hello-listview.jd
@@ -1,36 +1,88 @@
-page.title=Hello, ListView
+page.title=List View
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>A {@link android.widget.ListView} is a View that shows items in a vertically scrolling list. The items are
- acquired from a {@link android.widget.ListAdapter}.</p>
+<p>{@link android.widget.ListView} is a {@link android.view.ViewGroup} that creates a list of
+scrollable items. The list items are automatically inserted to the list using a {@link
+android.widget.ListAdapter}.</p>
+<p>In this tutorial, you'll create a scrollable list of country names that are read from a string array.
+When a list item is selected, a toast message will display the position of the item in the list.</p>
<ol>
- <li>Start a new project/ListActivity called HelloListView.</li>
- <li>Open the HelloListView Java file. Make the class extend ListActivity (instead of Activity).
+ <li>Start a new project named <em>HelloListView</em>.</li>
+ <li>Create an XML file named <code>list_item.xml</code> and save it inside the
+<code>res/layout/</code> folder. Insert the following:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="10dp"
+ android:textSize="16sp" >
+</TextView>
+</pre>
+ <p>This file defines the layout for each item that will be placed in the {@link
+android.widget.ListView}.</p>
+ </li>
+
+ <li>Open the <code>HelloListView.java</code> and make the class extend {@link
+android.app.ListActivity} (instead of {@link android.app.Activity}):
<pre>public class HelloListView extends ListActivity {</pre>
</li>
- <li>Insert the following for the <code>onCreate()</code> method:
+ <li>Insert the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+method:
<pre>
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- setListAdapter(new ArrayAdapter<String>(this,
- android.R.layout.simple_list_item_1, COUNTRIES));
- getListView().setTextFilterEnabled(true);
+
+ setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, COUNTRIES));
+
+ ListView lv = getListView();
+ lv.setTextFilterEnabled(true);
+
+ lv.setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView<?> parent, View view,
+ int position, long id) {
+ // When clicked, show a toast with the TextView text
+ Toast.makeText(getApplicationContext(), ((TextView) view).getText(),
+ Toast.LENGTH_SHORT).show();
+ }
+ });
}
</pre>
- <p>Notice that we don't need to load a layout (at least, not in this case, because we're using
- the whole screen for our list). Instead, we just call <code>setListAdapter()</code> (which automatically
- adds a ListView to the ListActivity), and provide it with an ArrayAdapter that binds a
- <code>simple_list_item_1</code> layout item to each entry in the <code>COUNTRIES</code>
- array (added next). The next line of code adds a text filter to the ListView, so that when the user
- begins typing, the list will filter the entire view to display only the items that match the entry.</p>
- </li>
- <li>Following the <code>onCreate()</code> method, add the String array:
+<p>Notice that this does not load a layout file for the Activity (which you usually do with {@link
+android.app.Activity#setContentView(int)}). Instead, {@link
+android.app.ListActivity#setListAdapter(ListAdapter)} automatically
+adds a {@link android.widget.ListView} to fill the entire screen of the {@link
+android.app.ListActivity}. This method takes an {@link android.widget.ArrayAdapter}, which
+manages the array of list items that will be placed into the {@link android.widget.ListView}. The
+{@link android.widget.ArrayAdapter} constructor takes the application {@link
+android.content.Context}, the
+layout description for each list item (created in
+the previous step), and a {@link java.util.List} of objects to insert in the {@link
+android.widget.ListView} (defined next).</p>
+
+<p>The {@link android.widget.ListView#setTextFilterEnabled(boolean)} method turns on text filtering
+for the {@link android.widget.ListView}, so that when the user begins typing, the list will be
+filtered.</p>
+
+<p>The {@link android.widget.ListView#setOnItemClickListener(OnItemClickListener)} method defines
+the on-click listener for each item. When an item in the {@link android.widget.ListView} is clicked,
+the {@link android.widget.AdapterView.OnItemClickListener#onItemClick(AdapterView,View,int,long)
+onItemClick()} method is called and a {@link android.widget.Toast} message is displayed, using the
+text from the clicked item.</p>
+
+<p class="note"><strong>Tip:</strong> You can use list item designs provided by the platform
+instead of defining your own layout file for the {@link android.widget.ListAdapter}. For example,
+try using <code>android.R.layout.simple_list_item_1</code> instead of
+<code>R.layout.list_item</code>.</p>
+</li>
+
+<li>After the {@link android.app.Activity#onCreate(Bundle) onCreate()} method, add the string
+array:
<pre>
static final String[] COUNTRIES = new String[] {
"Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
@@ -76,12 +128,40 @@
"Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
};
</pre>
+ <p>This is the array of strings that will be placed into the {@link android.widget.ListView}.</p>
</li>
-<li> Run it.</li>
+<li>Run the application.</li>
</ol>
-<p>You can scroll the list, or type to filter it. You should see something like this:</p>
+<p>You can scroll the list, or type to filter it, then click an item to see a message. You
+should see something like this:</p>
<img src="images/hello-listview.png" width="150px" />
+<p>Note that using a hard-coded string array is not the best design practice. One is
+used in this tutorial for simplicity, in order to demonstrate the {@link
+android.widget.ListView} widget. The better practice is to reference a string array
+defined by an external resource, such as with a {@code <string-array>}
+resource in your project {@code res/values/strings.xml} file. For example:</p>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string-array name="countries_array">
+ <item>Bahrain</item>
+ <item>Bangladesh</item>
+ <item>Barbados</item>
+ <item>Belarus</item>
+ <item>Belgium</item>
+ <item>Belize</item>
+ <item>Benin</item>
+ </string-array>
+</resources>
+</pre>
+<p>To use these resource strings for the {@link android.widget.ArrayAdapter}, replace the original
+{@link android.app.ListActivity#setListAdapter(ListAdapter)} line with the following:</p>
+<pre>
+String[] countries = getResources().getStringArray(R.array.countries_array);
+setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, countries));
+</pre>
+
<h3>References</h3>
<ul>
<li>{@link android.widget.ListView}</li>
diff --git a/docs/html/resources/tutorials/views/hello-mapview.jd b/docs/html/resources/tutorials/views/hello-mapview.jd
index 531300f..a3fa548 100644
--- a/docs/html/resources/tutorials/views/hello-mapview.jd
+++ b/docs/html/resources/tutorials/views/hello-mapview.jd
@@ -1,273 +1,303 @@
-page.title=Hello, MapView
+page.title=Google Map View
parent.title=Hello, Views
parent.link=index.html
@jd:body
+<p>Using the Google Maps library, you can create your own map-viewing Activity. In this
+tutorial, you'll create a simple map application in two parts. In Part 1, you'll create an app that
+shows a map the user can pan and zoom. In Part 2, you'll add overlay items that mark
+points of interest.</p>
+
<div class="special">
-<p>This tutorial requires that you have the Google Maps external library
-installed in your SDK environment. By default the Android SDK includes the
-Google APIs add-on, which in turn includes the Maps external library. If you
-don't have the Google APIs SDK add-on, you can download it from this
-location:</p>
+<p>This tutorial requires that you have the external Google Maps library
+installed in your SDK environment. The Maps library is included with the Google APIs
+add-on, which you can install using the Android SDK and
+AVD Manager. To learn how, see <a href="{@docRoot}sdk/adding-components.html">Adding SDK
+Components</a>.</p>
-<p style="margin-left:2em;"><a
-href="http://code.google.com/android/add-ons/google-apis">http://code.google.com/android/add-ons/google-apis</a></p>
-
-<p>The Google APIs add-on requires Android 1.5 SDK or later release. After
-installing the add-on in your SDK, set your project properties to use the build
-target called "Google APIs Add-on". See the instructions for setting a build
+<p>After installing the Google APIs add-on in your SDK, set your project properties to use the build
+target called "Google APIs by Google Inc.". See the instructions for setting a build
target in <a href="{@docRoot}guide/developing/eclipse-adt.html">Developing in
Eclipse with ADT</a> or <a
href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>,
as appropriate for your environment. </p>
-<p>You will also need to use the android tool to set up an AVD that uses the
-Google APIs deployment target. See <a
+<p>You will also need to set up a new AVD that uses the same Google APIs deployment target. See <a
href="{@docRoot}guide/developing/tools/avd.html">Android Virtual Devices</a> for
-more information. Once you have set up your environment, you will be able to
-build and run the project described in this tutorial</a></p>
+more information.</p>
+
+<p>For reference material, see the <a
+href="http://code.google.com/android/add-ons/google-apis/reference/index.html">Google Maps
+library documentation</a>.</p>
</div>
-<p>A MapView allows you to create your own map-viewing Activity.
-First, we'll create a simple Activity that can view and navigate a map. Then we will add some overlay items.</p>
+<h2>Part 1: Creating a Map Activity</h2>
<ol>
- <li>Start a new project/Activity called HelloMapView.
+ <li>Start a new project named <em>HelloGoogleMaps</em>.</li>
- <li>Because we're using the Google Maps library,
- which is not a part of the standard Android library, we need to
- declare it in the Android Manifest. Open the AndroidManifest.xml
- file and add the following as a child of the <code><application></code> element:
+ <li>Because the Maps library is not a part of the standard Android library, you must
+ declare it in the Android Manifest. Open the <code>AndroidManifest.xml</code>
+ file and add the following as a child of the <code><application></code> element:
+ <pre><uses-library android:name="com.google.android.maps" /></pre>
+ </li>
- <pre><uses-library android:name="com.google.android.maps" /></pre>
- </li>
- <li>We also need access to the internet in order to retrieve the Google Maps tiles,
- so the application must request the {@link android.Manifest.permission#INTERNET INTERNET} permissions.
- In the manifest file, add the following as a child of the <code><manifest></code> element:
- <pre><uses-permission android:name="android.permission.INTERNET" /></pre>
- </li>
- <li>Now open the main layout file for your project. Define a layout with a com.google.android.maps.MapView
- inside a android.widget.RelativeLayout:
+ <li>You also need access to the Internet in order to retrieve map tiles,
+ so you must also request the {@link android.Manifest.permission#INTERNET} permission.
+ In the manifest file, add the following as a child of the <code><manifest></code> element:
+ <pre><uses-permission android:name="android.permission.INTERNET" /></pre>
+ </li>
+ <li>While you're in the manifest, give the map some more space by getting rid of the title bar
+with the "NoTitleBar" theme:
+<pre>
+<activity android:name=".HelloGoogleMaps" android:label="@string/app_name"
+ <strong>android:theme="@android:style/Theme.NoTitleBar"</strong>>
+</pre>
+ </li>
+
+
+ <li>Open the <code>res/layout/main.xml</code> file and add a single
+ {@code com.google.android.maps.MapView} as the root node:
<pre>
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/mainlayout"
- android:orientation="vertical"
+<com.google.android.maps.MapView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/mapview"
android:layout_width="fill_parent"
- android:layout_height="fill_parent" >
-
- <com.google.android.maps.MapView
- android:id="@+id/mapview"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:clickable="true"
- android:apiKey="<em>Your Maps API Key</em>"
- />
-
-</RelativeLayout>
+ android:layout_height="fill_parent"
+ android:clickable="true"
+ android:apiKey="<em>Your Maps API Key goes here</em>"
+/>
</pre>
- <p>The <code>clickable</code> attribute defines whether you want to allow user-interaction with the map.
- In this case, we set it "true" so that the user can navigate.</p>
+ <p>The <code>android:clickable</code> attribute defines whether you want to allow
+ user-interaction with the map. If this is "false" then touching the map does nothing.</p>
- <p>The <code>apiKey</code> attribute holds the Google Maps API Key that proves your application and signer
- certificate has been registered with the Google Maps service. Because MapView uses Google Maps data, this key is required
- in order to receive the map data, even while you are developing. Registration is free and it only takes a couple
- minutes to register your certificate and receive a Maps API Key. For instructions on getting a key, read
- <a href="http://code.google.com/android/add-ons/google-apis/mapkey.html">Obtaining a Maps API Key</a>.
- (For the purpose of this tutorial, you should register with the fingerprint of the SDK debug certificate.)
- Once you've acquired the Maps API Key, insert it for the <code>apiKey</code> value.</p></li>
+ <p>The <code>android:apiKey</code> attribute holds the Maps API Key for your
+ application, which proves your application and signer certificate has been registered with the
+ Maps service. This is required in order to receive the map data, even while you are
+ developing. Registration to the service is free and it only takes a couple
+ minutes to register your certificate and get a Maps API Key.</p>
+ <p>Go now to get a key. For instructions, read
+ <a href="http://code.google.com/android/add-ons/google-apis/mapkey.html">Obtaining a Maps API
+ Key</a>. For the purpose of this tutorial, you should <a
+ href="http://code.google.com/android/add-ons/google-apis/mapkey.html#getdebugfingerprint">register
+ with the SDK debug certificate</a>, which will only be valid while your application is signed
+ with the debug key (once you sign with your private key, you will need a new API key).
+ When you get your key, insert it for the value of <code>android:apiKey</code>.</p>
+ </li>
- <li>Now open the HelloMapView.java file. For this Activity, we're going to extend the special sub-class of
- Activity called MapActivity, so change the class declaration to extend
- MapActicity, instead of Activity:</p>
+ <li>Now open the <code>HelloGoogleMaps.java</code> file. For this Activity, extend
+ {@code MapActivity} (instead of {@code android.app.Activity}):</p>
- <pre>public class HelloMapView extends MapActivity {</pre>
+ <pre>public class HelloGoogleMaps extends MapActivity {</pre>
+ <p>This is a special sub-class of {@link android.app.Activity}, provided by the Maps
+ library, which provides important map capabilities.</p>
- <li>The <code>isRouteDisplayed()</code> method is required, so add it inside the class:
+ <li>Inside every {@code MapActivity}, the <code>isRouteDisplayed()</code> method is required, so
+ override this method:
<pre>
@Override
protected boolean isRouteDisplayed() {
return false;
}
</pre>
-<p>You can actually run this now, but all it does is allow you to pan around the map.</p>
-<p>Android provides a handy {@link android.widget.ZoomControls} widget for zooming in and out of a View.
-MapView can automatically hook one for us by requesting it with the <code>getZoomControls()</code>
-method. Let's do this.</p>
+<p>This method is required for some accounting from the Maps service to see if you're currently
+displaying any route information. In this case, you're not, so return false.</p>
+</li>
-<li>Go back to the layout file. We need a new ViewGroup element, in which we'll
- place the ZoomControls. Just below the MapView element (but inside the RelativeLayout), add this element:
+<li>Now add the standard {@link android.app.Activity#onCreate(Bundle) onCreate()} callback method
+to the class:
<pre>
-<LinearLayout
- android:id="@+id/zoomview"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBottom="@id/mapview"
- android:layout_centerHorizontal="true"
-/></pre>
-
- <p>It doesn't really matter what kind of ViewGroup we use, because we just want a
- container that we can position within our root RelativeLayout.</p>
-
- <p>The last two attributes are available only to an element that's a child of a
- RelativeLayout. <code>layout_alignBottom</code> aligns the bottom of this element to the bottom of
- the element identified with a resource tag (which must be a sibling to this element).
- <code>layout_centerHorizontal</code> centers this on the horizontal plane.</p></li>
-
- <li>Now go back to the HelloMapView class. We'll now retrieve the ZoomControls object from
- the MapView and add it to our new layout element. First, at the top of the HelloMapView,
- instantiate handles for the MapView and LinearLayout, plus a ZoomControl object:
+@Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+}
+</pre>
+<p>This loads the layout file created above. In fact, this is now a workable application that will
+display map tiles and allow the user to pan around the map. But there's no ability to zoom.
+Fortunately, there's a very simple zoom feature built into the {@code MapView} class, which you can
+summon with {@code setBuiltInZoomControls(boolean)}. Do this at the end of the {@link
+android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
<pre>
-LinearLayout linearLayout;
-MapView mapView;
-ZoomControls mZoom;</pre></li>
-
- <li>Then initialize each of these in <code>onCreate()</code>. We'll capture the LinearLayout and
- MapView through their layout resources. Then get the ZoomControls from the MapView::
-<pre>
-linearLayout = (LinearLayout) findViewById(R.id.zoomview);
-mapView = (MapView) findViewById(R.id.mapview);
-mZoom = (ZoomControls) mapView.getZoomControls();</pre>
-
- <p>By using the ZoomControls object provided by MapView, we don't have to do any of the work
- required to actually perform the zoom operations. The ZoomControls widget that MapView
- returns for us is already hooked into the MapView and works as soon as we add it to the
- layout. The controls will appear whenever the user touches the map, then dissapear after
- a few moments of inactivity.</p></li>
-
- <li>Now just plug our ZoomControls into the LinearLayout we added:
-
- <pre>linearLayout.addView(mZoom);</pre></li>
-
- <li>Run it.</li>
+ MapView mapView = (MapView) findViewById(R.id.mapview);
+ mapView.setBuiltInZoomControls(true);
+</pre>
+</li>
+<li>That's all there is to it. Run the application. (Remember, you must have an <a
+href="{@docRoot}guide/developing/tools/avd.html">AVD</a> configured to use the Google APIs
+target, or be using a development device that includes the Maps library.)</li>
</ol>
-<hr/>
+<h2>Part 2: Adding Overlay Items</h2>
-<p>So, we now have full interaction controls. All well and good, but what we really want our map
-for is custom markers and layovers. Let's add some Overlay
-objects to our map. To do this, we're going to
-implement the ItemizedOverlay
-class, which can manage a whole set of Overlay items for us.</p>
+<p>So, now you have a map, but in many cases you'll also want to create your own map
+markers and lay-overs. That's what you'll do now. In order to do so, you must implement the
+{@code ItemizedOverlay} class, which can manage a whole set of {@code Overlay} (which are the
+individual items placed on the map).</p>
<ol>
- <li>Create a new Java class named HelloItemizedOverlay that implements ItemizedOverlay.
+ <li>Create a new Java class named <code>HelloItemizedOverlay</code> that implements
+ {@code ItemizedOverlay}.
- <p>When using Eclipse, right-click the package name in the Eclipse Package Explorer, and select New > Class. Fill-in
- the Name field as <em>HelloItemizedOverlay</em>. For the Superclass, enter
- <em>com.google.android.maps.ItemizedOverlay</em>. Click the checkbox for <em>Constructors from
- superclass</em>. Click Finish.</p></li>
+ <p>When using Eclipse, right-click the package name in the Eclipse Package Explorer, and
+ select <strong>New > Class</strong>. Fill-in
+ the Name field as <em>HelloItemizedOverlay</em>. For the Superclass, enter
+ "com.google.android.maps.ItemizedOverlay". Click the checkbox for <em>Constructors from
+ superclass</em>. Click Finish.</p></li>
- <li> First thing, we need an OverlayItem ArrayList, in which we'll put each of the OverlayItem
- objects we want on our map. Add this at the top of the HelloItemizedOverlay class:
+ <li>First, you need an <code>OverlayItem</code> {@link java.util.ArrayList}, in which you'll put
+ each of the <code>OverlayItem</code> objects you want on the map. Add this at the top of the
+ <code>HelloItemizedOverlay</code> class:
- <pre>private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();</pre></li>
+ <pre>private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();</pre>
+ </li>
- <li>All the constructor does is define the default marker to be used on each of the OverlayItems.
- In order for the Drawable to actually get drawn, it must have its bounds defined. And we want the
- center-point at the bottom of the image to be the point at which it's attached to the map
- coordinates. We handle all this with the boundCenterBottom() method. Wrap this around our
- defaultMarker, so the super constructor call looks like this:
+ <li>Now define the <code>HelloItemizedOverlay</code> constructors. The constructor must
+ define the default marker for each of the {@code OverlayItem}s. In order for the {@link
+ android.graphics.drawable.Drawable} to actually get drawn, it must have its bounds defined. Most
+ commonly, you want the center-point at the bottom of the image to be the point at which it's
+ attached to the map coordinates. This is handled for you with the {@code boundCenterBottom()}
+ method. Wrap this around our defaultMarker, so the super constructor call looks like this:
+<pre>
+public HelloItemizedOverlay(Drawable defaultMarker) {
+ super(boundCenterBottom(defaultMarker));
+}
+</pre>
+ </li>
- <pre>super(boundCenterBottom(defaultMarker));</pre></li>
-
- <li>In order to add new OverlayItems to our ArrayList, we need a new public method. We'll handle
- this with the following method:
-
+ <li>In order to add new {@code OverlayItem}s to the ArrayList, you need a new method:
<pre>
public void addOverlay(OverlayItem overlay) {
mOverlays.add(overlay);
populate();
}</pre>
+ <p>Each time you add a new {@code OverlayItem} to the ArrayList, you must call
+ <code>populate()</code> for the {@code ItemizedOverlay}, which will read each of the
+ {@code OverlayItem}s and prepare them to be drawn.</p>
+ </li>
- <p>Each time we add a new OverlayItem, we must call <code>populate()</code>, which will read each of out
- OverlayItems and prepare them to be drawn.</p></li>
-
- <li>In order for the <code>populate()</code> method to read each OverlayItem, it will make a request to
- <code>createItem(int)</code>. We must define this method to properly read from our ArrayList. Replace the
- existing contents of the createItem method with a <code>get()</code> call to our ArrayList:
+ <li>When the <code>populate()</code> method executes, it will call <code>createItem(int)</code> in
+ the {@code ItemizedOverlay} to retrieve each {@code OverlayItem}. You must override this method to
+ properly read from the ArrayList and return the {@code OverlayItem} from the position specified
+ by the given integer. Your override method should look like this:
<pre>
@Override
protected OverlayItem createItem(int i) {
return mOverlays.get(i);
}
-</pre></li>
+</pre>
+ </li>
- <li>We're also required to override the <code>size()</code> method. Replace the existing contents of the
- method with a size request to our ArrayList:
+ <li>You must also override the <code>size()</code> method to return the current number of
+ items in the ArrayList:
+<pre>
+@Override
+public int size() {
+ return mOverlays.size();
+}
+</pre>
+ </li>
- <pre>return mOverlays.size();</pre></li>
+ <li>Now set up the ability to handle touch events on the overlay items. First, you're
+ going to need a reference to the application {@link android.content.Context} as a member of
+ this class. So add {@code Context mContext} as a class member, then initialize it with a
+ new class constructor:
+<pre>
+public HelloItemizedOverlay(Drawable defaultMarker, Context context) {
+ super(defaultMarker);
+ mContext = context;
+}
+</pre>
+ <p>This passes the {@code defaultMarker} up to the default constructor to bound its coordinates
+ and then initialize {@code mContext} with the given {@link android.content.Context}.</p>
+
+ <p>Then override the {@code onTap(int)} callback method, which will handle the event
+ when an item is tapped by the user:</p>
+<pre>
+@Override
+protected boolean onTap(int index) {
+ OverlayItem item = mOverlays.get(index);
+ AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
+ dialog.setTitle(item.getTitle());
+ dialog.setMessage(item.getSnippet())
+ dialog.show();
+ return true;
+}
+</pre>
+ <p>This uses the member {@code android.content.Context} to create a new {@link
+android.app.AlertDialog.Builder} and uses the tapped {@code OverlayItem}'s title and snippet for
+the dialog's title and message text. (You'll see the {@code OverlayItem} title and snippet defined
+when you create it below.)</p>
+ </li>
+
</ol>
+<p>You're now done with the <code>HelloItemizedOverlay</code> class and can start using it
+to add items on the map.</p>
-<p>That's it for the HelloItemizedOverlay class. We're now ready to use it.</p>
-
-<hr/>
-<p>Go back to the HelloMapView
-class. We'll start by creating one OverlayItem, adding to an instance of our HelloItemizedOverlay,
-and then adding this to the MapView.</p>
+<p>Go back to the <code>HelloGoogleMaps</code> class. In the following procedure, you'll create an
+{@code OverlayItem} and add it to an instance of the <code>HelloItemizedOverlay</code> class, then
+add the <code>HelloItemizedOverlay</code> to the <code>MapView</code> using a {@code GeoPoint}
+to define its coordinates on the map.</p>
<img src="images/androidmarker.png" align="right" />
-<p>First, we need the image that we'll use for our map overlay. Here, we'll use the Android on the
-right as our marker. Drag this image (or your own) to the res/drawable/ directory of your project workspace.</p>
-
-<p>Now we're ready to work in the HelloMapView:</p>
-
<ol>
- <li>First we need some more types. Add the following at the top of the HelloMapView class:
+ <li>First, you need the image for the map overlay. If you don't have one handy, use the Android on
+ the right. Drag this image (or your own) into the <code>res/drawable/</code> directory of your
+ project.</li>
+
+ <li>At the end of your existing {@code onCreate()} method, instantiate :
<pre>
-List<Overlay> mapOverlays;
-Drawable drawable;
-HelloItemizedOverlay itemizedOverlay;</pre></li>
+List<Overlay> mapOverlays = mapView.getOverlays();
+Drawable drawable = this.getResources().getDrawable(R.drawable.androidmarker);
+HelloItemizedOverlay itemizedoverlay = new HelloItemizedOverlay(drawable);</pre>
- <li>Now pick up where we left off in the <code>onCreate()</code> method. Instantiate the
- new fields:
+ <p>All overlay elements on a map are held by the {@code MapView}, so when you want to add some,
+ you have to get a list from the <code>getOverlays()</code> method. Then instantiate the {@link
+ android.graphics.drawable.Drawable} used for the map marker, which was saved in the {@code
+ res/drawable/} directory. The constructor for {@code HelloItemizedOverlay} (your custom {@code
+ ItemizedOverlay}) takes the Drawable in order to set the default marker for all overlay
+ items.</p>
+ </li>
-<pre>
-mapOverlays = mapView.getOverlays();
-drawable = this.getResources().getDrawable(R.drawable.androidmarker);
-itemizedoverlay = new HelloItemizedOverlay(drawable);</pre>
-
- <p>All overlay elements on a map are held by the MapView, so when we want to add some, we must
- first retrieve the List with <code>getOverlays()</code> methods. We instantiate the Drawable, which will
- be used as our map marker, by using our Context resources to get the Drawable we placed in
- the res/drawable/ directory (androidmarker.png). Our HelloItemizedOverlay takes the Drawable in order to set the
- default marker.</p></li>
-
- <li>Now let's make our first OverlayItem by creating a GeoPoint
- that defines our map coordinates, then pass it to a new OverlayItem:
-
+ <li>Now create a {@code GeoPoint} that defines the map coordinates for the first overlay item,
+ and pass it to a new {@code OverlayItem}:
<pre>
GeoPoint point = new GeoPoint(19240000,-99120000);
-OverlayItem overlayitem = new OverlayItem(point, "", "");</pre>
+OverlayItem overlayitem = new OverlayItem(point, "Hola, Mundo!", "I'm in Mexico City!");
+</pre>
- <p>GeoPoint coordinates are based in microdegrees (degrees * 1e6). The OverlayItem takes this
- GeoPoint and two strings. Here, we won't concern ourselves with the strings, which can display
- text when we click our marker, because we haven't yet written the click handler for the OverlayItem.</p></li>
+ <p>{@code GeoPoint} coordinates are specified in microdegrees (<code>degrees * 1e6</code>). The
+ {@code OverlayItem} constructor accepts the {@code GeoPoint} location, a string for the
+ item's title, and a string for the item's snippet text, respectively.</p>
+ </li>
- <li>All that's left is for us to add this OverlayItem to our collection in the HelloItemizedOverlay,
- and add this to the List of Overlay objects retrieved from the MapView:
-
+ <li>All that's left is to add this {@code OverlayItem} to your collection in the
+ {@code HelloItemizedOverlay} instance, then add the {@code HelloItemizedOverlay} to the MapView:
<pre>
itemizedoverlay.addOverlay(overlayitem);
-mapOverlays.add(itemizedoverlay);</pre></li>
+mapOverlays.add(itemizedoverlay);
+</pre>
+ </li>
- <li>Run it!</li>
+ <li>Now run the application.</li>
</ol>
-<p>We've sent our droid to Mexico City. Hola, Mundo!</p>
<p>You should see the following:</p>
<img src="images/hello-mapview.png" width="150px" />
+<p>When you tap the overlay item, you'll see the dialog appear.</p>
-<p>Because we created our ItemizedOverlay class with an ArrayList, we can continue adding new
-OverlayItems. Try adding another one. Before the <code>addOverlay()</code> method is called, add these lines:</p>
+<p>Because the {@code ItemizedOverlay} class uses an {@code java.util.ArrayList} for all of the
+{@code OverlayItem}s, it's easy to add more. Try adding another one. Before the
+<code>addOverlay()</code> method is called, add these lines:</p>
<pre>
GeoPoint point2 = new GeoPoint(35410000, 139460000);
-OverlayItem overlayitem2 = new OverlayItem(point2, "", "");
+OverlayItem overlayitem2 = new OverlayItem(point2, "Sekai, konichiwa!", "I'm in Japan!");
</pre>
-<p>Run it again... We've sent a new droid to Tokyo. Sekai, konichiwa!</p>
+<p>Run the application again. (You probably need to move the map to find the new overlay item.)</p>
diff --git a/docs/html/resources/tutorials/views/hello-relativelayout.jd b/docs/html/resources/tutorials/views/hello-relativelayout.jd
index 1b91537..adc1179 100644
--- a/docs/html/resources/tutorials/views/hello-relativelayout.jd
+++ b/docs/html/resources/tutorials/views/hello-relativelayout.jd
@@ -1,33 +1,38 @@
-page.title=Hello, RelativeLayout
+page.title=Relative Layout
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>A {@link android.widget.RelativeLayout} is a ViewGroup that allows you to layout child elements
-in positions relative to the parent or siblings elements.</p>
+<p>{@link android.widget.RelativeLayout} is a {@link android.view.ViewGroup} that displays
+child {@link android.view.View} elements in relative positions. The position of a {@link
+android.view.View} can be specified as relative to sibling elements (such as to the left-of or below
+a given element) or in positions relative to the {@link android.widget.RelativeLayout} area (such as
+aligned to the bottom, left of center).</p>
+
+<p>A {@link android.widget.RelativeLayout} is a very powerful utility for designing a user
+interface because it can eliminate nested {@link android.view.ViewGroup}s. If you find
+yourself using several nested {@link android.widget.LinearLayout} groups, you may be able to
+replace them with a single {@link android.widget.RelativeLayout}.</p>
<ol>
- <li>Start a new project/Activity called HelloRelativeLayout.</li>
- <li>Open the layout file. Make it like so:
+ <li>Start a new project named <em>HelloRelativeLayout</em>.</li>
+ <li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
-
<TextView
android:id="@+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Type here:"/>
-
<EditText
android:id="@+id/entry"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:layout_below="@id/label"/>
-
<Button
android:id="@+id/ok"
android:layout_width="wrap_content"
@@ -36,39 +41,48 @@
android:layout_alignParentRight="true"
android:layout_marginLeft="10dip"
android:text="OK" />
-
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/ok"
android:layout_alignTop="@id/ok"
android:text="Cancel" />
-
</RelativeLayout>
</pre>
-<p>Pay attention to each of the additional <code>layout_*</code> attributes (besides the
-usual width and height, which are required for all elements). When using relative layout,
-we use attributes like <code>layout_below</code> and <code>layout_toLeftOf</code> to describe
-how we'd like to position each View. Naturally, these are different relative positions, and the
-value of the attribute is the id of the element we want the position relative to.</p>
+<p>Notice each of the <code>android:layout_*</code> attributes, such as <code>layout_below</code>,
+<code>layout_alignParentRight</code>, and <code>layout_toLeftOf</code>. When using a {@link
+android.widget.RelativeLayout}, you can use these attributes to describe
+how you want to position each {@link android.view.View}. Each one of these attributes define a
+different kind
+of relative position. Some attributes use the resource ID of a sibling {@link android.view.View}
+to define its own relative position. For example, the last {@link android.widget.Button} is
+defined to lie to the left-of and aligned-with-the-top-of the {@link android.view.View}
+identified by the ID <code>ok</code> (which is the previous {@link android.widget.Button}).</p>
+<p>All of the available layout attributes are defined in {@link
+android.widget.RelativeLayout.LayoutParams}.</p>
</li>
-<li>Make sure your Activity loads this layout in the <code>onCreate()</code> method:</p>
+<li>Make sure you load this layout in the
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
</pre>
-<p><code>R.layout.main</code> refers to the <code>main.xml</code> layout file.</p>
+<p>The {@link android.app.Activity#setContentView(int)} method loads the
+layout file for the {@link android.app.Activity}, specified by the resource
+ID — <code>R.layout.main</code> refers to the <code>res/layout/main.xml</code> layout
+file.</p>
</li>
-<li>Run it.</li>
+<li>Run the application.</li>
</ol>
-<p>You should see the following:</p>
+<p>You should see the following layout:</p>
<img src="images/hello-relativelayout.png" width="150px" />
<h3>Resources</h3>
<ul>
<li>{@link android.widget.RelativeLayout}</li>
+ <li>{@link android.widget.RelativeLayout.LayoutParams}</li>
<li>{@link android.widget.TextView}</li>
<li>{@link android.widget.EditText}</li>
<li>{@link android.widget.Button}</li>
diff --git a/docs/html/resources/tutorials/views/hello-spinner.jd b/docs/html/resources/tutorials/views/hello-spinner.jd
index 3a04214..7a3a9c3 100644
--- a/docs/html/resources/tutorials/views/hello-spinner.jd
+++ b/docs/html/resources/tutorials/views/hello-spinner.jd
@@ -1,17 +1,17 @@
-page.title=Hello, Spinner
+page.title=Spinner
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>A {@link android.widget.Spinner} is a widget that allows the user to select an item from a group.
-It is similar to a dropdown list and will allow scrolling when the
-list exceeds the available vertical space on the screen.</p>
+<p>{@link android.widget.Spinner} is a widget similar to a drop-down list for selecting items.</p>
+<p>In this tutorial, you'll create
+a simple spinner widget that displays a list of planets. When one is selected, a toast message
+will display the selected item.</p>
<ol>
- <li>Start a new project/Activity called HelloSpinner.</li>
- <li>Open the layout file.
- Make it like so:
+ <li>Start a new project named <em>HelloSpinner</em>.</li>
+ <li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -19,41 +19,34 @@
android:padding="10dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
-
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
- android:text="Please select a planet:"
+ android:text="@string/planet_prompt"
/>
-
<Spinner
android:id="@+id/spinner"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:drawSelectorOnTop="true"
android:prompt="@string/planet_prompt"
/>
-
</LinearLayout>
</pre>
- <p>Notice that the Spinner's <code>android:prompt</code> is a string resource. In
- this case, Android does not allow it to be a string, it must be a reference to a resource.
- So...</p>
+ <p>Notice that the {@link android.widget.TextView}'s <code>android:text</code> attribute and the
+{@link android.widget.Spinner}'s <code>android:prompt</code> attribute both reference the same
+string resource. This text behaves as a title for the widget. When applied to the {@link
+android.widget.Spinner}, the title text will appear
+in the selection dialog that appears upon selecting the widget.</p>
</li>
-<li>Open the strings.xml file in res/values/ and add the following <code><string></code>
-element inside the <code><resources></code> element:
+<li>Create a <code>strings.xml</code> file in <code>res/values/</code> and edit the file to look
+like this:
<pre>
-<string name="planet_prompt">Choose a planet</string>
-</pre>
-</li>
-
-<li>Create a new XML file in res/values/ called arrays.xml. Insert the following:
-<pre>
+<?xml version="1.0" encoding="utf-8"?>
<resources>
-
- <string-array name="planets">
+ <string name="planet_prompt">Choose a planet</string>
+ <string-array name="planets_array">
<item>Mercury</item>
<item>Venus</item>
<item>Earth</item>
@@ -63,35 +56,85 @@
<item>Uranus</item>
<item>Neptune</item>
</string-array>
-
</resources>
</pre>
- <p>This is the list of items (planets) that the user can select from in the Spinner widget.</p>
+ <p>The {@code <string>} element defines the title string referenced by the {@link
+android.widget.TextView} and {@link android.widget.Spinner} in the layout above. The {@code
+<string-array} element defines the list of strings that will be displayed as the list in
+the {@link android.widget.Spinner} widget.</p>
</li>
-<li>Now open the HelloSpinner.java file. Insert the following code into the HelloSpinner class:
+<li>Now open the <code>HelloSpinner.java</code> file and insert the following code for the {@link
+android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
- Spinner s = (Spinner) findViewById(R.id.spinner);
- ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
- this, R.array.planets, android.R.layout.simple_spinner_item);
+ Spinner spinner = (Spinner) findViewById(R.id.spinner);
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
+ this, R.array.planets_array, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- s.setAdapter(adapter);
+ spinner.setAdapter(adapter);
}
</pre>
- <p>That's it. We start by creating a Spinner from our layout. We then create an {@link android.widget.ArrayAdapter}
- that binds each element of our string array to a layout view—we pass <code>createFromResource</code> our Context,
- the array of selectable items and the type of layout we'd like each one bound to. We then call
- <code>setDropDownViewResource()</code> to define the type of layout in which to present the
- entire collection. Finally, we set this Adapter to associate with our Spinner,
- so the string items have a place to go.</p>
+ <p>After the {@code main.xml} layout is set as the content view, the {@link
+android.widget.Spinner} widget is captured from the layout with {@link
+android.app.Activity#findViewById(int)}. The {@link
+android.widget.ArrayAdapter#createFromResource(Context,int,int) createFromResource()} method then
+creates a new {@link android.widget.ArrayAdapter}, which binds each item in the string array
+to the initial appearance for the {@link android.widget.Spinner} (which is how each item will
+appear in the spinner when selected). The {@code
+R.array.planets_array} ID references the {@code string-array} defined above and the
+{@code android.R.layout.simple_spinner_item} ID references a layout for the standard spinner
+appearance, defined by the platform. Then {@link
+android.widget.ArrayAdapter#setDropDownViewResource(int)} is called to define the appearance for
+each item when the widget is opened ({@code simple_spinner_dropdown_item} is
+another standard layout defined by the platform). Finally, the {@link android.widget.ArrayAdapter}
+is set to associate all of its items with the {@link android.widget.Spinner} by calling {@link
+android.widget.AdapterView#setAdapter(T)}.</p>
</li>
-<li>Now run it.</li>
+<li>Now create a nested class that implements {@link
+android.widget.AdapterView.OnItemSelectedListener}. This will provide a callback method that will
+notify your application when an item has been selected from the {@link
+android.widget.Spinner}. Here's what this class should look like:
+<pre>
+public class MyOnItemSelectedListener implements OnItemSelectedListener {
+
+ public void onItemSelected(AdapterView<?> parent,
+ View view, int pos, long id) {
+ Toast.makeText(parent.getContext()), "The planet is " +
+ parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
+ }
+
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Do nothing.
+ }
+}
+</pre>
+<p>The {@link android.widget.AdapterView.OnItemSelectedListener} requires the {@link
+android.widget.AdapterView.OnItemSelectedListener#onItemSelected(AdapterView,View,int,long)
+onItemSelected()} and {@link
+android.widget.AdapterView.OnItemSelectedListener#onNothingSelected(AdapterView)
+onNothingSelected()} callback methods. The former is called when an item from the {@link
+android.widget.AdapterView} is selected, in which case, a short {@link android.widget.Toast}
+message displays the selected text; and the latter is called when a selection disappears from the
+{@link android.widget.AdapterView}, which doesn't happen in this case, so it's ignored.</p>
+</li>
+
+<li>Now the {@code MyOnItemSelectedListener} needs to be applied to the {@link
+android.widget.Spinner}. Go back to the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+method and add the following line to the end:
+<pre>
+ spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
+</pre>
+<p>This creates a new anonymous instance of the {@code MyOnItemSelectedListener} and sets it as the
+listener for the {@link android.widget.Spinner}.</p>
+</li>
+
+<li>Run the application.</li>
</ol>
<p>It should look like this:</p>
<img src="images/hello-spinner.png" width="150px" />
diff --git a/docs/html/resources/tutorials/views/hello-tablelayout.jd b/docs/html/resources/tutorials/views/hello-tablelayout.jd
index 83d6f5d..c8c5982 100644
--- a/docs/html/resources/tutorials/views/hello-tablelayout.jd
+++ b/docs/html/resources/tutorials/views/hello-tablelayout.jd
@@ -1,16 +1,15 @@
-page.title=Hello, TableLayout
+page.title=Table Layout
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>A {@link android.widget.TableLayout} is a ViewGroup that
-will lay child View elements into rows and columns.</p>
+<p>{@link android.widget.TableLayout} is a {@link android.view.ViewGroup} that
+displays child {@link android.view.View} elements in rows and columns.</p>
<ol>
- <li>Start a new project/Activity called HelloTableLayout.</li>
- <li>Open the layout file.
- Make it like so:
+ <li>Start a new project named <em>HelloTableLayout</em>.</li>
+ <li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -89,21 +88,28 @@
</TableRow>
</TableLayout>
</pre>
-<p>Notice how this resembles the structure of an HTML table. <code>TableLayout</code> is like the
-<code>table</code> element; <code>TableRow</code> is like a <code>tr</code> element; but for our cells like
-the html <code>td</code> element, we can use any kind of View. Here, we use <code>TextView</code> for the cells.</p>
+<p>Notice how this resembles the structure of an HTML table. The {@link android.widget.TableLayout}
+element is like the HTML <code><table></code> element; {@link android.widget.TableRow} is like
+a <code>><tr>></code> element;
+but for the cells, you can use any kind of {@link android.view.View} element. In this example, a
+{@link android.widget.TextView} is used for each cell. In between some of the rows, there is also a
+basic {@link android.view.View}, which is used to draw a horizontal line.</p>
</li>
-<li>Make sure your Activity loads this layout in the <code>onCreate()</code> method:
+<li>Make sure your <em>HelloTableLayout</em> Activity loads this layout in the
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method:
<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
</pre>
-<p><code>R.layout.main</code> refers to the <code>main.xml</code> layout file.</p>
+<p>The {@link android.app.Activity#setContentView(int)} method loads the
+layout file for the {@link android.app.Activity}, specified by the resource
+ID — <code>R.layout.main</code> refers to the <code>res/layout/main.xml</code> layout
+file.</p>
</li>
-<li>Run it.</li>
+<li>Run the application.</li>
</ol>
<p>You should see the following:</p>
<img src="images/hello-tablelayout.png" width="150px" />
diff --git a/docs/html/resources/tutorials/views/hello-tabwidget.jd b/docs/html/resources/tutorials/views/hello-tabwidget.jd
index 8424616..199ceef 100644
--- a/docs/html/resources/tutorials/views/hello-tabwidget.jd
+++ b/docs/html/resources/tutorials/views/hello-tabwidget.jd
@@ -1,14 +1,72 @@
-page.title=Hello, TabWidget
+page.title=Tab Layout
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>A {@link android.widget.TabWidget} offers the ability to easily draw an interface that uses
-tabs to navigate between different views.</p>
+<p>To create a tabbed UI, you need to use a {@link android.widget.TabHost} and a {@link
+android.widget.TabWidget}. The {@link android.widget.TabHost} must be the root node for the layout,
+which contains both the {@link android.widget.TabWidget} for displaying the tabs and a {@link
+android.widget.FrameLayout} for displaying the tab content.</p>
+
+<p>You can implement your tab content in one of two ways: use the tabs to swap
+{@link android.view.View}s within the same {@link android.app.Activity}, or use the tabs to change
+between entirely separate activities. Which method you want for your application will depend on your
+demands, but if each tab provides a distinct user activity, then it probably makes sense to use
+a separate {@link android.app.Activity} for each tab, so that you can better manage the application
+in discrete groups, rather than one massive application and layout.</p>
+
+<p>In this tutorial, you'll create a tabbed UI that uses a separate {@link
+android.app.Activity} for each tab.</p>
<ol>
- <li>Start a new project/Activity called HelloTabWidget.</li>
- <li>Open the layout file and make it like so:</li>
+ <li>Start a new project named <em>HelloTabWidget</em>.</li>
+ <li>First, create three separate {@link android.app.Activity} classes in your project:
+<code>ArtistsActivity</code>, <code>AlbumsActivity</code>, and <code>SongsActivity</code>. These
+will each represent a separate tab. For now, make each one display a simple message using a {@link
+android.widget.TextView}. For example:
+<pre>
+public class ArtistsActivity extends Activity {
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ TextView textview = new TextView(this);
+ textview.setText("This is the Artists tab");
+ setContentView(textview);
+ }
+}
+</pre>
+ <p>Notice that this doesn't use a layout file. Just create a {@link
+android.widget.TextView}, give it some text and set that as the content. Duplicate this for
+each of the three activities.</p>
+
+ <li>You're going to need an icon for each of your tabs. And for each one, you should create an
+image for two different states: one for when the tab is selected, and one for when it is not. The
+general design recommendation is for the selected tab icon to be a darker color (grey), and the
+non-selected icon to be lighter (white). For example:
+ <p>
+ <img src="images/ic_tab_artists_white.png" title="ic_tab_artists_white.png" alt="" />
+ <img src="images/ic_tab_artists_grey.png" title="ic_tab_artists_grey.png" alt="" />
+ </p>
+ <p>Copy these images for use in this tutorial. Save them into your project
+<code>res/drawable/</code> directory. You now need to create a {@link
+android.graphics.drawable.Drawable} with XML that specifies which image
+to use for each state. Create a new file in <code>res/drawable/</code> named
+<code>ic_tab_artists.xml</code> and insert the following:</p>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- When selected, use grey -->
+ <item android:drawable="@drawable/ic_tab_artists_grey"
+ android:state_selected="true" />
+ <!-- When not selected, use white-->
+ <item android:drawable="@drawable/ic_tab_artists_white" />
+</selector>
+</pre>
+ <p>This is an XML definition for a {@link android.graphics.drawable.Drawable}, which you will
+reference as the image for a tab. When the image state changes, the image will automatically
+switch between the images defined here.</p>
+
+ <li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
@@ -18,7 +76,8 @@
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
- android:layout_height="fill_parent">
+ android:layout_height="fill_parent"
+ android:padding="5dp">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
@@ -26,94 +85,111 @@
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <TextView
- android:id="@+id/textview1"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:text="this is a tab" />
- <TextView
- android:id="@+id/textview2"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:text="this is another tab" />
- <TextView
- android:id="@+id/textview3"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:text="this is a third tab" />
- </FrameLayout>
+ android:layout_height="fill_parent"
+ android;padding="5dp" />
</LinearLayout>
</TabHost>
</pre>
- <p>Here, we've created a {@link android.widget.TabHost} that contains the entire layout of the Activity.
- A TabHost requires two descendant elements: a {@link android.widget.TabWidget} and a {@link android.widget.FrameLayout}.
- In order to properly layout these elements, we've put them inside a vertical {@link android.widget.LinearLayout}.
- The FrameLayout is where we keep the content that will change with each tab. Each child in the FrameLayout will
- be associated with a different tab.
- In this case, each tab simply shows a different {@link android.widget.TextView} with some text. </p>
- <p>Notice that the TabWidget and the FrameLayout elements have specific <code>android</code> namespace IDs. These are necessary
- so that the TabHost can automatically retireve references to them, populate the TabWidget with the tabs that we'll define
- in our code, and swap the views in the FrameLayout. We've also defined our own IDs for each TextView, which we'll use to
- associate each tab with the view that it should reveal.</p>
- <p>Of course, you can
- make these child views as large as complex as you'd like — instead of the TextView elements,
- you could start with other layout views and build a unique layout hierarchy for each tab.</p>
+ <p>This is the layout that will display the tabs and provide navigation between each {@link
+ android.app.Activity} created above.</p>
+ <p>The {@link android.widget.TabHost} requires that a {@link android.widget.TabWidget} and a
+{@link android.widget.FrameLayout} both live somewhere within it. To position the {@link
+android.widget.TabWidget} and {@link android.widget.FrameLayout} vertically, a {@link
+android.widget.LinearLayout} is used. The {@link android.widget.FrameLayout} is where the content
+for each tab goes, which is empty now because the {@link android.widget.TabHost} will automatically
+embed each {@link android.app.Activity} within it.</p>
+ <p>Notice that the {@link android.widget.TabWidget} and the {@link android.widget.FrameLayout}
+ elements have the IDs {@code tabs} and {@code tabcontent}, respectively. These names
+ must be used so that the {@link android.widget.TabHost} can retrieve references to each of
+ them. It expects exactly these names.</p>
</li>
- <li>Now we'll add our code. Open HelloTabWidget.java and make it a <code>TabActivity</code>.
- <p>By default, Eclipse creates a class that extends <code>Activity</code>. Change it to
- extend <code>TabActivity</code>:</p>
- <pre>
+
+ <li>Now open <code>HelloTabWidget.java</code> and make it extend {@link
+ android.app.TabActivity}:</p>
+<pre>
public class HelloTabWidget extends TabActivity {
-</pre>
+</pre>
</li>
- <li>Now fill in the the <code>onCreate</code> method like this:
- <pre>
+ <li>Use the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+ method:
+<pre>
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
- mTabHost = getTabHost();
-
- mTabHost.addTab(mTabHost.newTabSpec("tab_test1").setIndicator("TAB 1").setContent(R.id.textview1));
- mTabHost.addTab(mTabHost.newTabSpec("tab_test2").setIndicator("TAB 2").setContent(R.id.textview2));
- mTabHost.addTab(mTabHost.newTabSpec("tab_test3").setIndicator("TAB 3").setContent(R.id.textview3));
-
- mTabHost.setCurrentTab(0);
+ Resources res = getResources(); // Resource object to get Drawables
+ TabHost tabHost = getTabHost(); // The activity TabHost
+ TabHost.TabSpec spec; // Resusable TabSpec for each tab
+ Intent intent; // Reusable Intent for each tab
+
+ // Create an Intent to launch an Activity for the tab (to be reused)
+ intent = new Intent().setClass(this, ArtistsActivity.class);
+
+ // Initialize a TabSpec for each tab and add it to the TabHost
+ spec = tabHost.newTabSpec("artists").setIndicator("Artists",
+ res.getDrawable(R.drawable.ic_tab_artists))
+ .setContent(intent);
+ tabHost.addTab(spec);
+
+ // Do the same for the other tabs
+ intent = new Intent().setClass(this, AlbumsActivity.class);
+ spec = tabHost.newTabSpec("albums").setIndicator("Albums",
+ res.getDrawable(R.drawable.ic_tab_albums))
+ .setContent(intent);
+ mTabHost.addTab(spec);
+
+ intent = new Intent().setClass(this, SongsActivity.class);
+ spec = tabHost.newTabSpec("songs").setIndicator("Songs",
+ res.getDrawable(R.drawable.ic_tab_songs))
+ .setContent(intent);
+ tabHost.addTab(spec);
+
+ tabHost.setCurrentTab(getIntent());
}
</pre>
- <p>As usual, we start by setting our layout.</p>
- <p>We then call the TabActivity method <code>getTabHost()</code>,
- which returns us a reference to the TabHost we created in our layout. Upon our TabHost, we call <code>addTab()</code>
- for each of the tabs that we want to add to the TabWidget. Each time we call this, we pass a
- {@link android.widget.TabHost.TabSpec} that we build on the fly, and with it, chain together two necessary methods:
- <code>setIndicator()</code> to set the text for the tab button, and <code>setContent()</code> to define
- which View we want to associate with the tab and reveal when pressed. Our indicator is just a text string and
- our content is an ID reference to the TextView elements we inserted in the FrameLayout.</p>
- <p>At the end, we call <code>setCurrentTab()</code> to define which tab should be opened by default. The tabs
- are saved like a zero-based array, so to open the first tab, we pass zero (<var>0</var>).</p>
+ <p>This sets up each tab with their text and icon, and assigns each one an {@link
+android.app.Activity}.</p>
+ <p>A reference to the {@link android.widget.TabHost} is first captured with {@link
+android.app.TabActivity#getTabHost()}. Then, for
+each tab, a {@link android.widget.TabHost.TabSpec} is created to define the tab properties. The
+{@link android.widget.TabHost#newTabSpec(String)} method creates a new {@link
+android.widget.TabHost.TabSpec} identified by the given string tag. For each
+{@link android.widget.TabHost.TabSpec}, {@link
+android.widget.TabHost.TabSpec#setIndicator(CharSequence,Drawable)} is called to set the text and
+icon for the tab, and {@link android.widget.TabHost.TabSpec#setContent(Intent)} is called to specify
+the {@link android.content.Intent} to opens the appropriate {@link android.app.Activity}. Each
+{@link android.widget.TabHost.TabSpec} is then added to the {@link android.widget.TabHost} by
+calling {@link android.widget.TabHost#addTab(TabHost.TabSpec)}.</p>
+
+ <p>At the very end, {@link
+ android.widget.TabHost#setCurrentTab(int)} opens the tab to be displayed by default, specified
+ by the index position of the tab.</p>
+
+ <p>Notice that not once was the {@link android.widget.TabWidget} object referenced. This is
+ because a {@link android.widget.TabWidget} must always be a child of a {@link
+ android.widget.TabHost}, which is what you use for almost all interaction with the tabs. So when
+ a tab is added to the {@link android.widget.TabHost}, it's automatically added to the child
+ {@link android.widget.TabWidget}.</p>
</li>
- <li>To clean-up the presentation a bit more, let's remove the window title that appears at the top of the layout.
- Android includes a theme that removes that title for us. To add it, open the Android Manifest file and add
- the <var>NoTitleBar</var> theme to the <code><application></code> tag. It should end up like this:
- <pre>
-<application android:icon="@drawable/icon" android:theme="@android:style/Theme.NoTitleBar">
+
+ <li>Now open the Android Manifest file and add the <code>NoTitleBar</code> theme to the
+<em>HelloTabWidget</em>'s
+ <code><activity></code> tag. This will remove the default application title from the top
+ of the layout, leaving more space for the tabs, which effectively operate as their own titles.
+ The <code><activity></code> tag should look like this:
+<pre>
+<activity android:name=".HelloTabWidget" android:label="@string/app_name"
+ android:theme="@android:style/Theme.NoTitleBar">
</pre>
</li>
- <li>That's it. Run your application.</li>
+ <li>Run the application.</li>
</ol>
<p>Your application should look like this:</p>
<img src="images/hello-tabwidget.png" width="150px" />
-<div class="special"><p>You can include icons in your tabs by passing a
-{@link android.graphics.drawable.Drawable} when you call <code>setIndicator()</code>. Here's an example
-that uses a Drawable created from an image in the project resources:</p>
-<pre>setIndicator("TAB 1", getResources().getDrawable(R.drawable.tab_icon))</pre>
-</div>
-
<h3>References</h3>
<ul>
<li>{@link android.widget.TabWidget}</li>
diff --git a/docs/html/resources/tutorials/views/hello-timepicker.jd b/docs/html/resources/tutorials/views/hello-timepicker.jd
index 1a6c8f9..cf16c8e 100644
--- a/docs/html/resources/tutorials/views/hello-timepicker.jd
+++ b/docs/html/resources/tutorials/views/hello-timepicker.jd
@@ -1,106 +1,97 @@
-page.title=Hello, TimePicker
+page.title=Time Picker
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>A {@link android.widget.TimePicker} is a widget that allows the
-user to select the time by hour, minute and AM or PM.</p>
+<p>To provide a widget for selecting a time, use the {@link android.widget.TimePicker}
+widget, which allows the user to select the hour and minute in a familiar interface.</p>
+<p>In this tutorial, you'll create a {@link android.app.TimePickerDialog}, which presents the
+time picker in a floating dialog box at the press of a button. When the time is set by
+the user, a {@link android.widget.TextView} will update with the new date.</p>
<ol>
- <li>Start a new project/Activity called HelloTimePicker.</li>
- <li>Open the layout file and make it like so:
+ <li>Start a new project named <em>HelloTimePicker</em>.</li>
+ <li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
-
<TextView android:id="@+id/timeDisplay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""/>
-
<Button android:id="@+id/pickTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Change the time"/>
-
</LinearLayout>
</pre>
- <p>For the layout, we're using a vertical LinearLayout, with a {@link android.widget.TextView} that
- will display the time and a {@link android.widget.Button} that will initiate the
- {@link android.widget.TimePicker} dialog.
- With this layout, the TextView will sit above the Button.
- The text value in the TextView is set empty, as it will be filled by our Activity
- with the current time.</p>
- </li>
+<p>This is a basic {@link android.widget.LinearLayout} with a {@link android.widget.TextView}
+that will display the time and a {@link android.widget.Button} that will open the {@link
+android.app.TimePickerDialog}.</p>
+ </li>
- <li>Open HelloTimePicker.java. Insert the following to the HelloTimePicker class:
+ <li>Open <code>HelloTimePicker.java</code> and insert the following class members:
<pre>
-private TextView mTimeDisplay;
-private Button mPickTime;
+ private TextView mTimeDisplay;
+ private Button mPickTime;
-private int mHour;
-private int mMinute;
+ private int mHour;
+ private int mMinute;
-static final int TIME_DIALOG_ID = 0;
-
-@Override
-protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- // capture our View elements
- mTimeDisplay = (TextView) findViewById(R.id.timeDisplay);
- mPickTime = (Button) findViewById(R.id.pickTime);
-
- // add a click listener to the button
- mPickTime.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- showDialog(TIME_DIALOG_ID);
- }
- });
-
- // get the current time
- final Calendar c = Calendar.getInstance();
- mHour = c.get(Calendar.HOUR_OF_DAY);
- mMinute = c.get(Calendar.MINUTE);
-
- // display the current date
- updateDisplay();
-}
+ static final int TIME_DIALOG_ID = 0;
</pre>
-<p class="note"><strong>Tip:</strong> Press Ctrl(or Cmd) + Shift + O to import all needed packages.</p>
- <p>We start by instantiating variables for our View elements and time fields.
- The <code>TIME_DIALOG_ID</code> is a static integer that uniquely identifies the dialog. In the
- <code>onCreate()</code> method, we get prepared by setting the layout and capturing the View elements.
- We then set an on-click listener for the Button, so that when it is clicked, it will
- show our TimePicker dialog. The <code>showDialog()</code> method will perform a callback
- to our Activity. (We'll define this callback in the next section.) We then create an
- instance of {@link java.util.Calendar} and get the current hour and minute. Finally, we call
- <code>updateDisplay()</code>—our own method that will fill the TextView with the time.</p>
-</li>
-
-<li>After the <code>onCreate()</code> method, add the <code>onCreateDialog()</code> callback method:
+<p>This declares variables for the layout elements and time fields.
+The <code>TIME_DIALOG_ID</code> is a static integer that uniquely identifies the dialog.</p>
+ </li>
+ <li>Now insert the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+method:
<pre>
-@Override
-protected Dialog onCreateDialog(int id) {
- switch (id) {
- case TIME_DIALOG_ID:
- return new TimePickerDialog(this,
- mTimeSetListener, mHour, mMinute, false);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ // capture our View elements
+ mTimeDisplay = (TextView) findViewById(R.id.timeDisplay);
+ mPickTime = (Button) findViewById(R.id.pickTime);
+
+ // add a click listener to the button
+ mPickTime.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ showDialog(TIME_DIALOG_ID);
+ }
+ });
+
+ // get the current time
+ final Calendar c = Calendar.getInstance();
+ mHour = c.get(Calendar.HOUR_OF_DAY);
+ mMinute = c.get(Calendar.MINUTE);
+
+ // display the current date
+ updateDisplay();
}
- return null;
-}
</pre>
- <p>This is passed the identifier we gave <code>showDialog()</code> and initializes
- the TimePicker to the time we retrieved from our Calendar instance. It will be called by
- <code>showDialog()</code>.</p>
+
+<p>First, the content is set to the <code>main.xml</code> layout and then the {@link
+android.widget.TextView} and {@link android.widget.Button} are captured with {@link
+android.app.Activity#findViewById(int)}.
+Then an {@link android.view.View.OnClickListener} is created for the {@link android.widget.Button},
+so that when clicked, it will call {@link
+android.app.Activity#showDialog(int)}, passing the unique integer ID for the time picker
+dialog. Using {@link android.app.Activity#showDialog(int)} allows the {@link
+android.app.Activity} to manage the life-cycle of the dialog and will call the {@link
+android.app.Activity#onCreateDialog(int)} callback method to request the {@link android.app.Dialog}
+that should be displayed (which you'll define later). After the on-click listener is set, a new
+{@link java.util.Calendar} is created to get the current hour and minute. Finally, the
+private <code>updateDisplay()</code> method is called in order to fill the {@link
+android.widget.TextView} with the current time.</p>
</li>
-<li>Now add our <code>updateDisplay()</code> method:
+<li>Add the <code>updateDisplay()</code> and <code>pad()</code> methods:
<pre>
// updates the time we display in the TextView
private void updateDisplay() {
@@ -109,13 +100,22 @@
.append(pad(mHour)).append(":")
.append(pad(mMinute)));
}
+
+private static String pad(int c) {
+ if (c >= 10)
+ return String.valueOf(c);
+ else
+ return "0" + String.valueOf(c);
+}
</pre>
- <p>This simply takes our member fields for the time and inserts them in
- the <code>mTimeDisplay</code> TextView. Note that we call a new method, <code>pad()</code>,
- on the hour and minute. (We'll create this method in the last step.)</p>
+<p>The <code>updateDisplay()</code> method uses the member fields for the time and inserts them in
+the <code>mTimeDisplay</code> {@link android.widget.TextView}. The <code>pad()</code> method returns
+the appropriate string representation of the hour or minute—it will prefix a zero to the
+number if it's a single digit.</p>
</li>
-<li>Next, add a listener to be called when the time is reset:
+<li>Add a class member for a {@link android.app.TimePickerDialog.OnTimeSetListener} that will be
+called when the user sets a new time:
<pre>
// the callback received when the user "sets" the time in the dialog
private TimePickerDialog.OnTimeSetListener mTimeSetListener =
@@ -127,24 +127,31 @@
}
};
</pre>
- <p>Now when the user is done setting the time (clicks the "Set" button), we update our member fields with
- the new time and update our TextView.</p>
-</li>
-<li>Finally, add the <code>pad()</code> method that we called from the <code>updateDisplay()</code>:
-<pre>
-private static String pad(int c) {
- if (c >= 10)
- return String.valueOf(c);
- else
- return "0" + String.valueOf(c);
-}
-</pre>
- <p>This method returns the appropriate String representation of the hour or minute.
- It will prefix a zero to the number if it's a single digit.
- </p>
+<p>When the user is done setting the time (clicks the "Set" button), the
+<code>onTimeSet()</code> method is called and it updates the member fields with
+the new time and updates the layout's {@link android.widget.TextView}.</p>
</li>
-<li>Now run it.</li>
+<li>Add the {@link android.app.Activity#onCreateDialog(int)} callback method:
+<pre>
+@Override
+protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case TIME_DIALOG_ID:
+ return new TimePickerDialog(this,
+ mTimeSetListener, mHour, mMinute, false);
+ }
+ return null;
+}
+</pre>
+<p>This is an {@link android.app.Activity} callback that is passed the identifier you passed to
+{@link android.app.Activity#showDialog(int)}, in the {@link android.widget.Button}'s on-click
+listener. When the ID matches, this initializes the {@link android.app.TimePickerDialog} with the
+member variables initialized at the end of <code>onCreate()</code> and the {@link
+android.app.TimePickerDialog.OnTimeSetListener} created in the previous step.</p>
+</li>
+
+<li>Run the application.</li>
</ol>
<p>When you press the "Change the time" button, you should see the following:</p>
<img src="images/hello-timepicker.png" width="150px" />
@@ -152,6 +159,7 @@
<h3>References</h3>
<ol>
<li>{@link android.widget.TimePicker}</li>
+ <li>{@link android.app.TimePickerDialog.OnTimeSetListener}</li>
<li>{@link android.widget.Button}</li>
<li>{@link android.widget.TextView}</li>
<li>{@link java.util.Calendar}</li>
diff --git a/docs/html/resources/tutorials/views/hello-webview.jd b/docs/html/resources/tutorials/views/hello-webview.jd
index c4388ea..17b2646 100644
--- a/docs/html/resources/tutorials/views/hello-webview.jd
+++ b/docs/html/resources/tutorials/views/hello-webview.jd
@@ -1,63 +1,72 @@
-page.title=Hello, WebView
+page.title=Web View
parent.title=Hello, Views
parent.link=index.html
@jd:body
-<p>A {@link android.webkit.WebView} allows you to create your own web browser Activity. In this tutorial,
-we'll create a simple Activity that can view web pages.</p>
+<p>{@link android.webkit.WebView} allows you to create your own window for viewing web pages (or even
+develop a complete browser). In this tutorial, you'll create a simple {@link android.app.Activity}
+that can view and navigate web pages.</p>
<ol>
- <li>Create a new project/Activity called HelloWebView.</li>
- <li>Open the layout file. Insert a WebView so it looks like so:
+ <li>Create a new project named <em>HelloWebView</em>.</li>
+ <li>Open the <code>res/layout/main.xml</code> file and insert the following:
<pre>
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <WebView
- android:id="@+id/webview"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- />
-
-</LinearLayout>
-</pre></li>
-
- <li>Now open the HelloWebView.java file.
- At the top of the class, instantiate a WebView object:
-<pre>WebView webview;</pre>
- <p> Then add the following at the end of the <code>onCreate()</code> method:</p>
-<pre>
-webview = (WebView) findViewById(R.id.webview);
-webview.getSettings().setJavaScriptEnabled(true);
-webview.loadUrl("http://www.google.com");
+<WebView
+ android:id="@+id/webview"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+/>
</pre>
+ </li>
- <p>This captures the WebView we created in our layout, then requests a
- {@link android.webkit.WebSettings} object and enables JavaScript.
- Then we load a URL.</p></li>
+ <li>Now open the <code>HelloWebView.java</code> file.
+ At the top of the class, declare a {@link android.webkit.WebView} object:
+<pre>WebView mWebView;</pre>
+ <p>Then use the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+ method:</p>
+<pre>
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
- <li>Because we're accessing the internet, we need to add the appropriate
- permissions to the Android manifest file. So open the AndroidManifest.xml file
- and, add the following as a child of the <code><manifest></code> element:
+ mWebView = (WebView) findViewById(R.id.webview);
+ mWebView.getSettings().setJavaScriptEnabled(true);
+ mWebView.loadUrl("http://www.google.com");
+}
+</pre>
+ <p>This initializes the member {@link android.webkit.WebView} with the one from the
+ {@link android.app.Activity} layout; requests a {@link android.webkit.WebSettings} object with
+ {@link android.webkit.WebView#getSettings()}; and enables JavaScript for the {@link
+ android.webkit.WebView} with {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)}.
+ Finally, an initial web page is loaded with {@link
+ android.webkit.WebView#loadUrl(String)}.</p>
+ </li>
- <pre><uses-permission android:name="android.permission.INTERNET" /></pre></li>
+ <li>Because this application needs access to the Internet, you need to add the appropriate
+ permissions to the Android manifest file. Open the <code>AndroidManifest.xml</code> file
+ and add the following as a child of the <code><manifest></code> element:
- <li>Now run it.</li>
-</ol>
-<p> You now have the world's simplest web page viewer.
- It's not quite a browser yet. It only loads the page we've requested.</p>
+ <pre><uses-permission android:name="android.permission.INTERNET" /></pre></li>
-<hr/>
+ <li>While you're in the manifest, give some more space for web pages by removing the title
+ bar, with the "NoTitleBar" theme:
+<pre>
+<activity android:name=".HelloGoogleMaps" android:label="@string/app_name"
+ <strong>android:theme="@android:style/Theme.NoTitleBar"</strong>>
+</pre>
+ </li>
-<p>We can load a page, but as soon as we click a link, the default Android web browser
-handles the Intent, instead of our own WebView handling the action. So now we'll
-override the {@link android.webkit.WebViewClient} to enable us to handle our own URL loading.</p>
+ <li>Now run the application.
+ <p>You now have a simplest web page viewer.
+ It's not quite a browser yet because as soon as you click a link, the default Android Browser
+ handles the Intent to view a web page, because this {@link android.app.Activity} isn't
+ technically enabled to do so. Instead of adding an intent filter to view web pages, you can
+ override the {@link android.webkit.WebViewClient} class and enable this {@link
+ android.app.Activity} to handle its own URL requests.</p>
+ </li>
-<ol>
- <li>In the HelloAndroid Activity, add this nested private class:
+ <li>In the <code>HelloAndroid</code> Activity, add this nested class:
<pre>
private class HelloWebViewClient extends WebViewClient {
@Override
@@ -65,42 +74,51 @@
view.loadUrl(url);
return true;
}
-}</pre></li>
+}
+</pre>
+ </li>
+ <li>Then towards the end of the {@link android.app.Activity#onCreate(Bundle)} method, set an
+ instance of the <code>HelloWebViewClient</code> as the {@link android.webkit.WebViewClient}:
+ <pre>mWebView.setWebViewClient(new WebViewClientDemo());</pre>
- <li>Now, in the <code>onCreate()</code> method, set an instance of the <code>HelloWebViewClient</code>
- as our WebViewClient:
- <pre>webview.setWebViewClient(new WebViewClientDemo());</pre>
-
- <p>This line should immediately follow the initialization of our WebView object.</p>
- <p>What we've done is create a WebViewClient that will load any URL selected in our
-WebView in the same WebView. You can see this in the <code>shouldOverrideUrlLoading()</code>
-method, above—it is passed the current WebView and the URL, so all we do
-is load the URL in the given view. Returning <var>true</var> says that we've handled the URL
-ourselves and the event should not bubble-up.</p>
- <p>If you try it again, new pages will now load in the HelloWebView Activity. However, you'll notice that
-we can't navigate back. We need to handle the back button
-on the device, so that it will return to the previous page, rather than exit the application.</p>
+ <p>This line can go anywhere following the initialization of the {@link
+ android.webkit.WebView} object.</p>
+ <p>This creates a {@link android.webkit.WebViewClient} that will load any URL selected from this
+ {@link android.webkit.WebView} into the same {@link android.webkit.WebView}. The
+ {@link android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)} method is passed
+the current {@link android.webkit.WebView} and the URL requested, so all it needs to do is load
+the URL in the given view. Returning <code>true</code> says that the method has handled the URL
+and the event should not propagate (in which case, an Intent would be created that's handled by
+the Browser application).</p>
+ <p>If you run the application again, new pages will now load in this Activity.
+ However, you can't navigate back to previous pages. To do this, you need to handle the BACK
+ button on the device, so that it will return to the previous page, rather than exit the
+ application.</p>
</li>
- <li>To handle the back button key press, add the following method inside the HelloWebView
-Activity:
+ <li>To handle the BACK button key press, add the following method inside the
+ <code>HelloWebView</code> Activity:
<pre>
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {
- webview.goBack();
+ if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
+ mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
-}</pre>
- <p>The condition uses a {@link android.view.KeyEvent} to check
- whether the key pressed is the BACK button and whether the
- WebView is actually capable of navigating back (if it has a history). If both are
- <em>not</em> true, then we send the event up the chain (and the Activity will close).
- But if both <em>are</em> true, then we call <code>goBack()</code>,
- which will navigate back one step in the history. We then return true to indicate
- that we've handled the event.</p>
+}
+</pre>
+ <p>This {@link android.app.Activity#onKeyDown(int,KeyEvent)} callback method will be called
+ anytime a button is pressed while in the Activity. The condition inside uses the {@link
+ android.view.KeyEvent} to check whether the key pressed is the BACK button and whether the
+ {@link android.webkit.WebView} is actually capable of navigating back (if it has a history). If
+ both are true, then the {@link android.webkit.WebView#goBack()} method is called,
+ which will navigate back one step in the {@link android.webkit.WebView}
+ history.Returning <code>true</code> indicates that the event has been handled. If this condition
+ is not met, then the event is sent back to the system.</p>
</li>
+<li>Run the application again. You'll now be able to follow links and navigate back through the
+page history.</li>
</ol>
<p>When you open the application, it should look like this:</p>
<img src="images/hello-webview.png" width="150px" />
@@ -111,8 +129,3 @@
<li>{@link android.webkit.WebViewClient}</li>
<li>{@link android.view.KeyEvent}</li>
</ul>
-
-
-
-
-
diff --git a/docs/html/resources/tutorials/views/images/android_focused.png b/docs/html/resources/tutorials/views/images/android_focused.png
new file mode 100644
index 0000000..f84d0fe
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/android_focused.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/android_normal.png b/docs/html/resources/tutorials/views/images/android_normal.png
new file mode 100644
index 0000000..94a7084
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/android_normal.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/android_pressed.png b/docs/html/resources/tutorials/views/images/android_pressed.png
new file mode 100644
index 0000000..fe81ff9
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/android_pressed.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-autocomplete.png b/docs/html/resources/tutorials/views/images/hello-autocomplete.png
old mode 100755
new mode 100644
index e1fd80d..0b3e680
--- a/docs/html/resources/tutorials/views/images/hello-autocomplete.png
+++ b/docs/html/resources/tutorials/views/images/hello-autocomplete.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-formstuff.png b/docs/html/resources/tutorials/views/images/hello-formstuff.png
old mode 100755
new mode 100644
index 3b4bf54..949319f
--- a/docs/html/resources/tutorials/views/images/hello-formstuff.png
+++ b/docs/html/resources/tutorials/views/images/hello-formstuff.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-listview.png b/docs/html/resources/tutorials/views/images/hello-listview.png
old mode 100755
new mode 100644
index a1cf7aa..165b1ac
--- a/docs/html/resources/tutorials/views/images/hello-listview.png
+++ b/docs/html/resources/tutorials/views/images/hello-listview.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-mapview.png b/docs/html/resources/tutorials/views/images/hello-mapview.png
old mode 100755
new mode 100644
index 0956760..6bd9740
--- a/docs/html/resources/tutorials/views/images/hello-mapview.png
+++ b/docs/html/resources/tutorials/views/images/hello-mapview.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-tabwidget.png b/docs/html/resources/tutorials/views/images/hello-tabwidget.png
index 6a52356..6580c5b 100644
--- a/docs/html/resources/tutorials/views/images/hello-tabwidget.png
+++ b/docs/html/resources/tutorials/views/images/hello-tabwidget.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-webview.png b/docs/html/resources/tutorials/views/images/hello-webview.png
old mode 100755
new mode 100644
index 283ce7d..248c6d4
--- a/docs/html/resources/tutorials/views/images/hello-webview.png
+++ b/docs/html/resources/tutorials/views/images/hello-webview.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/ic_tab_artists_grey.png b/docs/html/resources/tutorials/views/images/ic_tab_artists_grey.png
new file mode 100644
index 0000000..9baa30e
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/ic_tab_artists_grey.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/ic_tab_artists_white.png b/docs/html/resources/tutorials/views/images/ic_tab_artists_white.png
new file mode 100644
index 0000000..3b010d5
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/ic_tab_artists_white.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/index.jd b/docs/html/resources/tutorials/views/index.jd
index 2cb5d3a..6ea7683 100644
--- a/docs/html/resources/tutorials/views/index.jd
+++ b/docs/html/resources/tutorials/views/index.jd
@@ -3,74 +3,95 @@
<style>
.view {float:left; margin:10px; font-size:120%; font-weight:bold;}
-.view img {border:1px solid black; margin:5px 0 0; padding:5px;}
+#jd-content .view img {border:1px solid black; margin:8px 0 0 0; padding:5px;}
</style>
-<p>This collection of "Hello World"-style tutorials is designed
-to get you quickly started with common Android Views and widgets. The aim is to let you copy and paste
-these kinds of boring bits so you can focus on developing the code that makes your Android application rock.
-Of course, we'll discuss some of the given code so that it all makes sense.</p>
+<p>This is a collection of "Hello World"-style tutorials designed
+to get you started quickly with common Android layouts and widgets.</p>
-<p>Note that a certain amount of knowledge is assumed for these tutorials. If you haven't
-completed the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello, World</a> tutorial,
-please do so—it will teach you many things you should know about basic
-Android development and Eclipse features. More specifically, you should know:</p>
+<p>A certain amount of knowledge is assumed for these tutorials. Before you start,
+you should have completed the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello,
+World</a> tutorial—it will teach you several things about basic
+Android development. More specifically, you should know:</p>
<ul>
- <li>How to create a new Android project.</li>
- <li>The basic structure of an Android project (resource files, layout files, etc.).</li>
- <li>The essential components of an {@link android.app.Activity}.</li>
- <li>How to build and run a project.</li>
+ <li>How to create an Android project and run it</li>
+ <li>The basic structure of an Android project (resource files, layout files, etc.)</li>
+ <li>The basic components of an {@link android.app.Activity}</li>
</ul>
-<p>Please, also notice that, in order to make these tutorials simple, some may
-not convey the better Android coding practices. In particular, many of them
-use hard-coded strings in the layout files—the better practice is to reference strings from
-your strings.xml file.</p>
-<p>With this knowledge, you're ready to begin, so take your pick.</p>
-<div>
+<p class="note"><strong>Note:</strong> In order to make these tutorials as simple as possible,
+some code may not conform to best practices for coding Android applications. In particular,
+hard-coded strings are used in places, when the better practice is to reference strings from a
+<code>res/values/strings.xml</code> resource file.</p>
+
+<p class="note"><strong>Tip:</strong> After you have pasted sample code into an Eclipse project,
+press <strong>Ctrl (or Cmd) + Shift + O</strong> to import the required packages.</p>
+
+<h2>Layouts</h2>
<div class="view">
-<a href="hello-linearlayout.html">LinearLayout</a><br/>
-<a href="hello-linearlayout.html"><img src="images/hello-linearlayout.png" height="285" width="200" /></a>
-</div>
-<div class="view">
-<a href="hello-relativelayout.html">RelativeLayout</a><br/>
-<a href="hello-relativelayout.html"><img src="images/hello-relativelayout.png" height="285" width="200" /></a>
-</div>
-<div class="view">
-<a href="hello-tablelayout.html">TableLayout</a><br/>
-<a href="hello-tablelayout.html"><img src="images/hello-tablelayout.png" height="285" width="200" /></a>
+<a href="hello-linearlayout.html">Linear Layout</a><br/>
+<a href="hello-linearlayout.html"><img src="images/hello-linearlayout.png" height="285" width="200"
+/></a>
</div>
<div class="view">
-<a href="hello-datepicker.html">DatePicker</a><br/>
-<a href="hello-datepicker.html"><img src="images/hello-datepicker.png" height="285" width="200" /></a>
+<a href="hello-relativelayout.html">Relative Layout</a><br/>
+<a href="hello-relativelayout.html"><img src="images/hello-relativelayout.png" height="285"
+width="200" /></a>
</div>
<div class="view">
-<a href="hello-timepicker.html">TimePicker</a><br/>
-<a href="hello-timepicker.html"><img src="images/hello-timepicker.png" height="285" width="200" /></a>
+<a href="hello-tablelayout.html">Table Layout</a><br/>
+<a href="hello-tablelayout.html"><img src="images/hello-tablelayout.png" height="285" width="200"
+/></a>
</div>
+
+<div class="view">
+<a href="hello-gridview.html">Grid View</a><br/>
+<a href="hello-gridview.html"><img src="images/hello-gridview.png" height="285" width="200" /></a>
+</div>
+
+<div class="view">
+<a href="hello-tabwidget.html">Tab Layout</a><br/>
+<a href="hello-tabwidget.html"><img src="images/hello-tabwidget.png" height="285" width="200" /></a>
+</div>
+
+<div class="view">
+<a href="hello-listview.html">List View</a><br/>
+<a href="hello-listview.html"><img src="images/hello-listview.png" height="285" width="200" /></a>
+</div>
+
+<p style="clear:left"> </p>
+
+<h2>Widgets & Other Views</h2>
+
+<div class="view">
+<a href="hello-datepicker.html">Date Picker</a><br/>
+<a href="hello-datepicker.html"><img src="images/hello-datepicker.png" height="285" width="200"
+/></a>
+</div>
+
+<div class="view">
+<a href="hello-timepicker.html">Time Picker</a><br/>
+<a href="hello-timepicker.html"><img src="images/hello-timepicker.png" height="285" width="200"
+/></a>
+</div>
+
<div class="view">
<a href="hello-formstuff.html">Form Stuff</a><br/>
<a href="hello-formstuff.html"><img src="images/hello-formstuff.png" height="285" width="200" /></a>
</div>
+
<div class="view">
<a href="hello-spinner.html">Spinner</a><br/>
<a href="hello-spinner.html"><img src="images/hello-spinner.png" height="285" width="200" /></a>
</div>
<div class="view">
-<a href="hello-autocomplete.html">AutoComplete</a><br/>
-<a href="hello-autocomplete.html"><img src="images/hello-autocomplete.png" height="285" width="200" /></a>
-</div>
-<div class="view">
-<a href="hello-listview.html">ListView</a><br/>
-<a href="hello-listview.html"><img src="images/hello-listview.png" height="285" width="200" /></a>
-</div>
-<div class="view">
-<a href="hello-gridview.html">GridView</a><br/>
-<a href="hello-gridview.html"><img src="images/hello-gridview.png" height="285" width="200" /></a>
+<a href="hello-autocomplete.html">Auto Complete</a><br/>
+<a href="hello-autocomplete.html"><img src="images/hello-autocomplete.png" height="285" width="200"
+/></a>
</div>
<div class="view">
@@ -79,40 +100,20 @@
</div>
<div class="view">
-<a href="hello-tabwidget.html">TabWidget</a><br/>
-<a href="hello-tabwidget.html"><img src="images/hello-tabwidget.png" height="285" width="200" /></a>
-</div>
-
-<div class="view">
-<a href="hello-mapview.html">MapView</a><br/>
+<a href="hello-mapview.html">Google Map View</a><br/>
<a href="hello-mapview.html"><img src="images/hello-mapview.png" height="285" width="200" /></a>
</div>
<div class="view">
-<a href="hello-webview.html">WebView</a><br/>
+<a href="hello-webview.html">Web View</a><br/>
<a href="hello-webview.html"><img src="images/hello-webview.png" height="285" width="200" /></a>
</div>
-<!--
-TODO
-
-<div class="view">
-<a href="hello-popupwindow.html">PopupWindow<br/>
-<img src="images/hello-popupwindow.png" height="285" width="200" /></a>
-</div>
-<div class="view">
-<a href="hello-tabhost.html">TabHost / TabWidget<br/>
-<img src="images/hello-tabhost.png" height="285" width="200" /></a>
-</div>
-ProgressBar; RatingBar; FrameLayout
-
--->
<p class="note" style="clear:left">
-There are plenty more Views and widgets available. See the {@link android.view.View} class
+There are plenty more layouts and widgets available. See the {@link android.view.View} class
for more on View layouts, and the {@link android.widget widget package}
for more useful widgets. And for more raw code samples, visit the
-<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/index.html">Api Demos</a>.
-These can also be found offline, in <code>/<sdk>/samples/ApiDemos</code>.</p>
-</div>
+<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/index.html">Api
+Demos</a>.</p>
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index f861afd..1d99c91 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -25,7 +25,7 @@
Development Tools (ADT), that is designed to give you a powerful,
integrated environment in which to build Android applications. </p>
-<p>ADT extends the capabilites of Eclipse to let you quickly set up new Android
+<p>ADT extends the capabilities of Eclipse to let you quickly set up new Android
projects, create an application UI, add components based on the Android
Framework API, debug your applications using the Android SDK tools, and even
export signed (or unsigned) APKs in order to distribute your application.</p>
@@ -77,7 +77,7 @@
<li>Click <strong>Add Site...</strong> </li>
<li>In the Add Site dialog that appears, enter this URL in the "Location" field:
<pre style="margin-left:0">https://dl-ssl.google.com/android/eclipse/</pre>
- <p>Note: If you have trouble aqcuiring the plugin, try using "http" in the Location URL,
+ <p>Note: If you have trouble acquiring the plugin, try using "http" in the Location URL,
instead of "https" (https is preferred for security reasons).</p>
<p>Click <strong>OK</strong>.</p></li>
<li>Back in the Available Software view, you should see the plugin listed by the URL,
@@ -94,13 +94,13 @@
<!-- 3.5 steps -->
<ol>
<li>Start Eclipse, then select <strong>Help</strong> > <strong>Install
- New Softare</strong>. </li>
+ New Software</strong>. </li>
<li>In the Available Software dialog, click <strong>Add...</strong>.</li>
<li>In the Add Site dialog that appears, enter a name for the remote site
(for example, "Android Plugin") in the "Name" field.
<p>In the "Location" field, enter this URL:</p>
<pre>https://dl-ssl.google.com/android/eclipse/</pre>
- <p>Note: If you have trouble aqcuiring the plugin, you can try
+ <p>Note: If you have trouble acquiring the plugin, you can try
using "http" in the URL, instead of "https" (https is preferred for
security reasons).</p>
<p>Click <strong>OK</strong>.</p>
diff --git a/docs/html/shareables/sample_images.zip b/docs/html/shareables/sample_images.zip
new file mode 100644
index 0000000..007a68a
--- /dev/null
+++ b/docs/html/shareables/sample_images.zip
Binary files differ
diff --git a/include/ui/ISurface.h b/include/ui/ISurface.h
index 2ca0026..b37a8ac 100644
--- a/include/ui/ISurface.h
+++ b/include/ui/ISurface.h
@@ -86,7 +86,7 @@
virtual void unregisterBuffers() = 0;
virtual sp<OverlayRef> createOverlay(
- uint32_t w, uint32_t h, int32_t format) = 0;
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp
index 8003d22..17db6f4 100644
--- a/libs/surfaceflinger/LayerBase.cpp
+++ b/libs/surfaceflinger/LayerBase.cpp
@@ -809,7 +809,7 @@
}
sp<OverlayRef> LayerBaseClient::Surface::createOverlay(
- uint32_t w, uint32_t h, int32_t format)
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation)
{
return NULL;
};
diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h
index ed07b3f..f73ea0c 100644
--- a/libs/surfaceflinger/LayerBase.h
+++ b/libs/surfaceflinger/LayerBase.h
@@ -351,7 +351,7 @@
virtual void postBuffer(ssize_t offset);
virtual void unregisterBuffers();
virtual sp<OverlayRef> createOverlay(uint32_t w, uint32_t h,
- int32_t format);
+ int32_t format, int32_t orientation);
protected:
friend class LayerBaseClient;
diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp
index 091856f..2735aa2 100644
--- a/libs/surfaceflinger/LayerBuffer.cpp
+++ b/libs/surfaceflinger/LayerBuffer.cpp
@@ -182,14 +182,15 @@
/**
* This creates an "overlay" source for this surface
*/
-sp<OverlayRef> LayerBuffer::createOverlay(uint32_t w, uint32_t h, int32_t f)
+sp<OverlayRef> LayerBuffer::createOverlay(uint32_t w, uint32_t h, int32_t f,
+ int32_t orientation)
{
sp<OverlayRef> result;
Mutex::Autolock _l(mLock);
if (mSource != 0)
return result;
- sp<OverlaySource> source = new OverlaySource(*this, &result, w, h, f);
+ sp<OverlaySource> source = new OverlaySource(*this, &result, w, h, f, orientation);
if (result != 0) {
mSource = source;
}
@@ -248,11 +249,11 @@
}
sp<OverlayRef> LayerBuffer::SurfaceLayerBuffer::createOverlay(
- uint32_t w, uint32_t h, int32_t format) {
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation) {
sp<OverlayRef> result;
sp<LayerBuffer> owner(getOwner());
if (owner != 0)
- result = owner->createOverlay(w, h, format);
+ result = owner->createOverlay(w, h, format, orientation);
return result;
}
@@ -370,8 +371,23 @@
LayerBuffer::BufferSource::~BufferSource()
{
+ class MessageDestroyTexture : public MessageBase {
+ SurfaceFlinger* flinger;
+ GLuint name;
+ public:
+ MessageDestroyTexture(
+ SurfaceFlinger* flinger, GLuint name)
+ : flinger(flinger), name(name) { }
+ virtual bool handler() {
+ glDeleteTextures(1, &name);
+ return true;
+ }
+ };
+
if (mTexture.name != -1U) {
- glDeleteTextures(1, &mTexture.name);
+ // GL textures can only be destroyed from the GL thread
+ mLayer.mFlinger->mEventQueue.postMessage(
+ new MessageDestroyTexture(mLayer.mFlinger.get(), mTexture.name) );
}
if (mTexture.image != EGL_NO_IMAGE_KHR) {
EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
@@ -444,6 +460,10 @@
NativeBuffer src(ourBuffer->getBuffer());
const Rect transformedBounds(mLayer.getTransformedBounds());
+ if (UNLIKELY(mTexture.name == -1LU)) {
+ mTexture.name = mLayer.createTexture();
+ }
+
#if defined(EGL_ANDROID_image_native_buffer)
if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) {
copybit_device_t* copybit = mLayer.mBlitEngine;
@@ -483,9 +503,6 @@
t.format = src.img.format;
t.data = (GGLubyte*)src.img.base;
const Region dirty(Rect(t.width, t.height));
- if (UNLIKELY(mTexture.name == -1LU)) {
- mTexture.name = mLayer.createTexture();
- }
mLayer.loadTexture(&mTexture, dirty, t);
}
@@ -566,11 +583,17 @@
void LayerBuffer::BufferSource::clearTempBufferImage() const
{
+ // delete the image
EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
- glDeleteTextures(1, &mTexture.name);
eglDestroyImageKHR(dpy, mTexture.image);
+
+ // and the associated texture (recreate a name)
+ glDeleteTextures(1, &mTexture.name);
Texture defaultTexture;
mTexture = defaultTexture;
+ mTexture.name = mLayer.createTexture();
+
+ // and the associated buffer
mTempGraphicBuffer.clear();
}
@@ -578,9 +601,9 @@
LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer,
sp<OverlayRef>* overlayRef,
- uint32_t w, uint32_t h, int32_t format)
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation)
: Source(layer), mVisibilityChanged(false),
- mOverlay(0), mOverlayHandle(0), mOverlayDevice(0)
+ mOverlay(0), mOverlayHandle(0), mOverlayDevice(0), mOrientation(orientation)
{
overlay_control_device_t* overlay_dev = mLayer.mFlinger->getOverlayEngine();
if (overlay_dev == NULL) {
@@ -662,8 +685,12 @@
if (mOverlay) {
overlay_control_device_t* overlay_dev = mOverlayDevice;
overlay_dev->setPosition(overlay_dev, mOverlay, x,y,w,h);
+ // we need to combine the layer orientation and the
+ // user-requested orientation.
+ Transform finalTransform = Transform(mOrientation) *
+ Transform(mLayer.getOrientation());
overlay_dev->setParameter(overlay_dev, mOverlay,
- OVERLAY_TRANSFORM, mLayer.getOrientation());
+ OVERLAY_TRANSFORM, finalTransform.getOrientation());
overlay_dev->commit(overlay_dev, mOverlay);
}
}
diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h
index 1b31435..e03f92c 100644
--- a/libs/surfaceflinger/LayerBuffer.h
+++ b/libs/surfaceflinger/LayerBuffer.h
@@ -74,7 +74,8 @@
status_t registerBuffers(const ISurface::BufferHeap& buffers);
void postBuffer(ssize_t offset);
void unregisterBuffers();
- sp<OverlayRef> createOverlay(uint32_t w, uint32_t h, int32_t format);
+ sp<OverlayRef> createOverlay(uint32_t w, uint32_t h, int32_t format,
+ int32_t orientation);
sp<Source> getSource() const;
sp<Source> clearSource();
@@ -150,7 +151,7 @@
public:
OverlaySource(LayerBuffer& layer,
sp<OverlayRef>* overlayRef,
- uint32_t w, uint32_t h, int32_t format);
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation);
virtual ~OverlaySource();
virtual void onDraw(const Region& clip) const;
virtual void onTransaction(uint32_t flags);
@@ -183,6 +184,7 @@
int32_t mFormat;
int32_t mWidthStride;
int32_t mHeightStride;
+ int32_t mOrientation;
mutable Mutex mOverlaySourceLock;
bool mInitialized;
};
@@ -200,7 +202,7 @@
virtual void unregisterBuffers();
virtual sp<OverlayRef> createOverlay(
- uint32_t w, uint32_t h, int32_t format);
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation);
private:
sp<LayerBuffer> getOwner() const {
return static_cast<LayerBuffer*>(Surface::getOwner().get());
diff --git a/libs/surfaceflinger/Transform.cpp b/libs/surfaceflinger/Transform.cpp
index 1501536..ab6f7ba 100644
--- a/libs/surfaceflinger/Transform.cpp
+++ b/libs/surfaceflinger/Transform.cpp
@@ -42,6 +42,17 @@
{
}
+Transform::Transform(int32_t flags) {
+ mTransform.reset();
+ int sx = (flags & FLIP_H) ? -1 : 1;
+ int sy = (flags & FLIP_V) ? -1 : 1;
+ if (flags & ROT_90) {
+ this->set(0, -sy, sx, 0);
+ } else {
+ this->set(sx, 0, 0, sy);
+ }
+}
+
Transform::~Transform() {
}
diff --git a/libs/surfaceflinger/Transform.h b/libs/surfaceflinger/Transform.h
index 78f5c19..ddab404 100644
--- a/libs/surfaceflinger/Transform.h
+++ b/libs/surfaceflinger/Transform.h
@@ -38,6 +38,7 @@
public:
Transform();
Transform(const Transform& other);
+ Transform(int32_t flags);
~Transform();
enum orientation_flags {
diff --git a/libs/ui/ISurface.cpp b/libs/ui/ISurface.cpp
index 4fb38ed..6f3d762 100644
--- a/libs/ui/ISurface.cpp
+++ b/libs/ui/ISurface.cpp
@@ -115,13 +115,14 @@
}
virtual sp<OverlayRef> createOverlay(
- uint32_t w, uint32_t h, int32_t format)
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation)
{
Parcel data, reply;
data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
data.writeInt32(w);
data.writeInt32(h);
data.writeInt32(format);
+ data.writeInt32(orientation);
remote()->transact(CREATE_OVERLAY, data, &reply);
return OverlayRef::readFromParcel(reply);
}
@@ -173,7 +174,8 @@
int w = data.readInt32();
int h = data.readInt32();
int f = data.readInt32();
- sp<OverlayRef> o = createOverlay(w, h, f);
+ int orientation = data.readInt32();
+ sp<OverlayRef> o = createOverlay(w, h, f, orientation);
return OverlayRef::writeToParcel(reply, o);
} break;
default:
diff --git a/libs/ui/Surface.cpp b/libs/ui/Surface.cpp
index 24ae27f..c7be05b 100644
--- a/libs/ui/Surface.cpp
+++ b/libs/ui/Surface.cpp
@@ -607,13 +607,21 @@
status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking)
{
if (mApiLock.tryLock() != NO_ERROR) {
- LOGE("calling Surface::lock() from different threads!");
+ LOGE("calling Surface::lock from different threads!");
CallStack stack;
stack.update();
stack.dump("Surface::lock called from different threads");
return WOULD_BLOCK;
}
+
+ /* Here we're holding mApiLock */
+ if (mLockedBuffer != 0) {
+ LOGE("Surface::lock failed, already locked");
+ mApiLock.unlock();
+ return INVALID_OPERATION;
+ }
+
// we're intending to do software rendering from this point
setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
@@ -682,8 +690,8 @@
status_t Surface::unlockAndPost()
{
if (mLockedBuffer == 0) {
- LOGE("unlockAndPost failed, no locked buffer");
- return BAD_VALUE;
+ LOGE("Surface::unlockAndPost failed, no locked buffer");
+ return INVALID_OPERATION;
}
status_t err = mLockedBuffer->unlock();
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 3a2f47a..fcb02f4 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -920,12 +920,17 @@
String path = c.getString(PATH_AUDIO_COLUMN_INDEX);
long lastModified = c.getLong(DATE_MODIFIED_AUDIO_COLUMN_INDEX);
- String key = path;
- if (mCaseInsensitivePaths) {
- key = path.toLowerCase();
+ // Only consider entries with absolute path names.
+ // This allows storing URIs in the database without the
+ // media scanner removing them.
+ if (path.startsWith("/")) {
+ String key = path;
+ if (mCaseInsensitivePaths) {
+ key = path.toLowerCase();
+ }
+ mFileCache.put(key, new FileCacheEntry(mAudioUri, rowId, path,
+ lastModified));
}
- mFileCache.put(key, new FileCacheEntry(mAudioUri, rowId, path,
- lastModified));
}
} finally {
c.close();
@@ -948,12 +953,17 @@
String path = c.getString(PATH_VIDEO_COLUMN_INDEX);
long lastModified = c.getLong(DATE_MODIFIED_VIDEO_COLUMN_INDEX);
- String key = path;
- if (mCaseInsensitivePaths) {
- key = path.toLowerCase();
+ // Only consider entries with absolute path names.
+ // This allows storing URIs in the database without the
+ // media scanner removing them.
+ if (path.startsWith("/")) {
+ String key = path;
+ if (mCaseInsensitivePaths) {
+ key = path.toLowerCase();
+ }
+ mFileCache.put(key, new FileCacheEntry(mVideoUri, rowId, path,
+ lastModified));
}
- mFileCache.put(key, new FileCacheEntry(mVideoUri, rowId, path,
- lastModified));
}
} finally {
c.close();
@@ -978,12 +988,17 @@
String path = c.getString(PATH_IMAGES_COLUMN_INDEX);
long lastModified = c.getLong(DATE_MODIFIED_IMAGES_COLUMN_INDEX);
- String key = path;
- if (mCaseInsensitivePaths) {
- key = path.toLowerCase();
- }
- mFileCache.put(key, new FileCacheEntry(mImagesUri, rowId, path,
- lastModified));
+ // Only consider entries with absolute path names.
+ // This allows storing URIs in the database without the
+ // media scanner removing them.
+ if (path.startsWith("/")) {
+ String key = path;
+ if (mCaseInsensitivePaths) {
+ key = path.toLowerCase();
+ }
+ mFileCache.put(key, new FileCacheEntry(mImagesUri, rowId, path,
+ lastModified));
+ }
}
} finally {
c.close();
@@ -1003,7 +1018,7 @@
if (c != null) {
try {
while (c.moveToNext()) {
- String path = c.getString(PATH_IMAGES_COLUMN_INDEX);
+ String path = c.getString(PATH_PLAYLISTS_COLUMN_INDEX);
if (path != null && path.length() > 0) {
long rowId = c.getLong(ID_PLAYLISTS_COLUMN_INDEX);
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index cc45114..f6cd46a 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -218,6 +218,8 @@
void AwesomePlayer::reset_l() {
cancelPlayerEvents();
+ mVideoRenderer.clear();
+
if (mLastVideoBuffer) {
mLastVideoBuffer->release();
mLastVideoBuffer = NULL;
@@ -243,8 +245,6 @@
delete mAudioPlayer;
mAudioPlayer = NULL;
- mVideoRenderer.clear();
-
mDurationUs = -1;
mFlags = 0;
mVideoWidth = mVideoHeight = -1;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index bd862e0..8cd572e 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -56,7 +56,7 @@
virtual void unregisterBuffers() {}
virtual sp<OverlayRef> createOverlay(
- uint32_t w, uint32_t h, int32_t format) {
+ uint32_t w, uint32_t h, int32_t format, int32_t orientation) {
return NULL;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 153a5ea..bc95b21 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -41,7 +41,6 @@
private AudioManager mAudioManager;
private IContentService mContentService;
private IPowerManager mPowerManager;
- private static final String[] PROVIDERS = { "gmail-ls", "calendar", "contacts" };
private boolean mSilent;
private boolean mVibrate;
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index fd42538..36da4eb 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -28,7 +28,6 @@
import android.app.DevicePolicyManager;
import android.app.IDevicePolicyManager;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -36,11 +35,9 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.IPowerManager;
-import android.os.PowerManager;
import android.os.RecoverySystem;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.provider.Settings;
import android.util.Log;
import android.util.Xml;
@@ -114,6 +111,12 @@
mContext.sendBroadcast(intent);
}
+ void sendAdminCommandLocked(String action) {
+ if (mActiveAdmin != null) {
+ sendAdminCommandLocked(mActiveAdmin, action);
+ }
+ }
+
ComponentName getActiveAdminLocked() {
if (mActiveAdmin != null) {
return mActiveAdmin.info.getComponent();
@@ -498,8 +501,7 @@
mActivePasswordMode = mode;
mActivePasswordLength = length;
mFailedPasswordAttempts = 0;
- sendAdminCommandLocked(mActiveAdmin,
- DeviceAdmin.ACTION_PASSWORD_CHANGED);
+ sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_CHANGED);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -515,8 +517,7 @@
long ident = Binder.clearCallingIdentity();
try {
mFailedPasswordAttempts++;
- sendAdminCommandLocked(mActiveAdmin,
- DeviceAdmin.ACTION_PASSWORD_FAILED);
+ sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_FAILED);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -532,8 +533,7 @@
long ident = Binder.clearCallingIdentity();
try {
mFailedPasswordAttempts = 0;
- sendAdminCommandLocked(mActiveAdmin,
- DeviceAdmin.ACTION_PASSWORD_SUCCEEDED);
+ sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_SUCCEEDED);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index a4abddb..7b66a76 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -24,12 +24,13 @@
import android.content.res.Resources;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.net.InterfaceConfiguration;
import android.os.INetworkManagementService;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
-
+import java.util.StringTokenizer;
import android.provider.Settings;
import android.content.ContentResolver;
import android.database.ContentObserver;
@@ -56,6 +57,7 @@
public static final int TetherStatusResult = 210;
public static final int IpFwdStatusResult = 211;
+ public static final int InterfaceGetCfgResult = 213;
}
/**
@@ -99,6 +101,29 @@
}
}
+ private static int stringToIpAddr(String addrString) throws UnknownHostException {
+ try {
+ String[] parts = addrString.split("\\.");
+ if (parts.length != 4) {
+ throw new UnknownHostException(addrString);
+ }
+
+ int a = Integer.parseInt(parts[0]) ;
+ int b = Integer.parseInt(parts[1]) << 8;
+ int c = Integer.parseInt(parts[2]) << 16;
+ int d = Integer.parseInt(parts[3]) << 24;
+
+ return a | b | c | d;
+ } catch (NumberFormatException ex) {
+ throw new UnknownHostException(addrString);
+ }
+ }
+
+ public static String intToIpString(int i) {
+ return ((i >> 24 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >> 8 ) & 0xFF) + "." +
+ (i & 0xFF);
+ }
+
//
// INetworkManagementService members
//
@@ -107,7 +132,53 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- return mConnector.doListCommand("list_interfaces", NetdResponseCode.InterfaceListResult);
+ return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
+ }
+
+ public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
+ String rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
+ Log.d(TAG, String.format("rsp <%s>", rsp));
+
+ // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3]
+ StringTokenizer st = new StringTokenizer(rsp);
+
+ try {
+ int code = Integer.parseInt(st.nextToken(" "));
+ if (code != NetdResponseCode.InterfaceGetCfgResult) {
+ throw new IllegalStateException(
+ String.format("Expected code %d, but got %d",
+ NetdResponseCode.InterfaceGetCfgResult, code));
+ }
+ } catch (NumberFormatException nfe) {
+ throw new IllegalStateException(
+ String.format("Invalid response from daemon (%s)", rsp));
+ }
+
+ InterfaceConfiguration cfg = new InterfaceConfiguration();
+ cfg.hwAddr = st.nextToken(" ");
+ try {
+ cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
+ } catch (UnknownHostException uhe) {
+ Log.e(TAG, "Failed to parse ipaddr", uhe);
+ cfg.ipAddr = 0;
+ }
+
+ try {
+ cfg.netmask = stringToIpAddr(st.nextToken(" "));
+ } catch (UnknownHostException uhe) {
+ Log.e(TAG, "Failed to parse netmask", uhe);
+ cfg.netmask = 0;
+ }
+ cfg.interfaceFlags = st.nextToken("]");
+ Log.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
+ return cfg;
+ }
+
+ public void setInterfaceConfig(
+ String iface, InterfaceConfiguration cfg) throws IllegalStateException {
+ String cmd = String.format("interface setcfg %s %s %s", iface,
+ intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
+ mConnector.doCommand(cmd);
}
public void shutdown() {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5b50a3a..44b6624 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -4610,7 +4610,8 @@
}
// Log the ANR to the event log.
- EventLog.writeEvent(EventLogTags.ANR, app.pid, app.processName, annotation);
+ EventLog.writeEvent(EventLogTags.AM_ANR, app.pid, app.processName, app.info.flags,
+ annotation);
// Dump thread traces as quickly as we can, starting with "interesting" processes.
ArrayList<Integer> pids = new ArrayList<Integer>(20);
@@ -8750,6 +8751,7 @@
EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
app == null ? "system" : (r == null ? "unknown" : r.processName),
+ r == null ? -1 : r.info.flags,
crashInfo.exceptionClassName,
crashInfo.exceptionMessage,
crashInfo.throwFileName,
@@ -8773,6 +8775,7 @@
EventLog.writeEvent(EventLogTags.AM_WTF, Binder.getCallingPid(),
app == null ? "system" : (r == null ? "unknown" : r.processName),
+ r == null ? -1 : r.info.flags,
tag, crashInfo.exceptionMessage);
addErrorToDropBox("wtf", r, null, null, tag, null, null, crashInfo);
@@ -8842,6 +8845,7 @@
sb.append("Process: system_server\n");
} else {
sb.append("Process: ").append(process.processName).append("\n");
+ sb.append("Flags: 0x").append(Integer.toString(process.info.flags, 16)).append("\n");
}
if (activity != null) {
sb.append("Activity: ").append(activity.shortComponentName).append("\n");
@@ -10448,7 +10452,7 @@
sInfo.applicationInfo.uid, sInfo.packageName,
sInfo.name);
}
- r = new ServiceRecord(ss, name, filter, sInfo, res);
+ r = new ServiceRecord(this, ss, name, filter, sInfo, res);
res.setService(r);
mServices.put(name, r);
mServicesByIntent.put(filter, r);
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index 952555b..0ddcc247 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -28,7 +28,7 @@
# An activity has been resumed and is now in the foreground:
30007 am_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3)
# Application Not Responding
-30008 anr (pid|1|5),(Package Name|3),(reason|3)
+30008 am_anr (pid|1|5),(Package Name|3),(Flags|1|5),(reason|3)
# Activity launch time
30009 activity_launch_time (Token|1|5),(Component Name|3),(time|2|3)
# Application process bound to work
@@ -80,6 +80,6 @@
30037 am_process_start_timeout (PID|1|5),(UID|1|5),(Process Name|3)
# Unhandled exception
-30039 am_crash (PID|1|5),(Process Name|3),(Exception|3),(Message|3),(File|3),(Line|1|5)
+30039 am_crash (PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5)
# Log.wtf() called
-30040 am_wtf (PID|1|5),(Process Name|3),(Tag|3),(Message|3)
+30040 am_wtf (PID|1|5),(Process Name|3),(Flags|1|5),(Tag|3),(Message|3)
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 2534410..89761a8 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -40,6 +40,7 @@
* A running application service.
*/
class ServiceRecord extends Binder {
+ final ActivityManagerService ams;
final BatteryStatsImpl.Uid.Pkg.Serv stats;
final ComponentName name; // service component.
final String shortName; // name.flattenToShortString().
@@ -192,8 +193,10 @@
}
}
- ServiceRecord(BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name,
+ ServiceRecord(ActivityManagerService ams,
+ BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name,
Intent.FilterComparison intent, ServiceInfo sInfo, Runnable restarter) {
+ this.ams = ams;
this.stats = servStats;
this.name = name;
shortName = name.flattenToShortString();
@@ -249,27 +252,46 @@
public void postNotification() {
if (foregroundId != 0 && foregroundNoti != null) {
- INotificationManager inm = NotificationManager.getService();
- if (inm != null) {
- try {
- int[] outId = new int[1];
- inm.enqueueNotification(packageName, foregroundId,
- foregroundNoti, outId);
- } catch (RemoteException e) {
+ // Do asynchronous communication with notification manager to
+ // avoid deadlocks.
+ final String localPackageName = packageName;
+ final int localForegroundId = foregroundId;
+ final Notification localForegroundNoti = foregroundNoti;
+ ams.mHandler.post(new Runnable() {
+ public void run() {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ int[] outId = new int[1];
+ inm.enqueueNotification(localPackageName, localForegroundId,
+ localForegroundNoti, outId);
+ } catch (RemoteException e) {
+ }
}
- }
+ });
}
}
public void cancelNotification() {
if (foregroundId != 0) {
- INotificationManager inm = NotificationManager.getService();
- if (inm != null) {
- try {
- inm.cancelNotification(packageName, foregroundId);
- } catch (RemoteException e) {
+ // Do asynchronous communication with notification manager to
+ // avoid deadlocks.
+ final String localPackageName = packageName;
+ final int localForegroundId = foregroundId;
+ ams.mHandler.post(new Runnable() {
+ public void run() {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotification(localPackageName, localForegroundId);
+ } catch (RemoteException e) {
+ }
}
- }
+ });
}
}
diff --git a/tests/AndroidTests/Android.mk b/tests/AndroidTests/Android.mk
index 757044f..a81b779 100644
--- a/tests/AndroidTests/Android.mk
+++ b/tests/AndroidTests/Android.mk
@@ -5,7 +5,7 @@
LOCAL_JAVA_LIBRARIES := framework-tests android.test.runner services
-LOCAL_STATIC_JAVA_LIBRARIES := googlelogin-client
+LOCAL_STATIC_JAVA_LIBRARIES := gsf-client
# Resource unit tests use a private locale
LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c 160dpi -c 32dpi -c 240dpi
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index eb422be..e0d8f79 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -52,9 +52,6 @@
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" />
- <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
- <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
- <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES" />
<!-- InstrumentationTestRunner for AndroidTests -->
<instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/tests/AndroidTests/src/com/android/unit_tests/GoogleLoginServiceTest.java b/tests/AndroidTests/src/com/android/unit_tests/GoogleLoginServiceTest.java
deleted file mode 100644
index 59f14bf..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/GoogleLoginServiceTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2008 The Android Open Source Project
-// All rights reserved.
-
-package com.android.unit_tests;
-
-import java.util.Arrays;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.Condition;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.Suppress;
-import android.util.Log;
-
-import com.google.android.googleapps.GoogleLoginCredentialsResult;
-import com.google.android.googleapps.IGoogleLoginService;
-import com.google.android.googlelogin.GoogleLoginServiceConstants;
-
-import junit.framework.Assert;
-
-// Suppress until bug http://b/issue?id=1416570 is fixed
-@Suppress
-/** Unit test for the Google login service. */
-public class GoogleLoginServiceTest extends AndroidTestCase {
- private static final String TAG = "GoogleLoginServiceTest";
-
- private IGoogleLoginService mGls = null;
- private Lock mGlsLock = new ReentrantLock();
- private Condition mGlsCv = mGlsLock.newCondition();
-
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- mGlsLock.lock();
- try {
- mGls = IGoogleLoginService.Stub.asInterface(service);
- mGlsCv.signalAll();
- } finally {
- mGlsLock.unlock();
- }
- Log.v(TAG, "service is connected");
- }
- public void onServiceDisconnected(ComponentName className) {
- mGlsLock.lock();
- try {
- mGls = null;
- mGlsCv.signalAll();
- } finally {
- mGlsLock.unlock();
- }
- Log.v(TAG, "service is disconnected");
- }
- };
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- getContext().bindService((new Intent())
- .setClassName("com.google.android.googleapps",
- "com.google.android.googleapps.GoogleLoginService"),
- mConnection, Context.BIND_AUTO_CREATE);
-
- // wait for the service to cnnnect
- mGlsLock.lock();
- try {
- while (mGls == null) {
- try {
- mGlsCv.await();
- } catch (InterruptedException ignore) {
- }
- }
- } finally {
- mGlsLock.unlock();
- }
- }
-
- @Override
- protected void tearDown() throws Exception {
- getContext().unbindService(mConnection);
- super.tearDown();
- }
-
- public void testSingleAccountScheme() throws Exception {
- Assert.assertNotNull(mGls);
- mGls.deleteAllAccounts();
-
- Assert.assertNull(mGls.getAccount(false));
- Assert.assertNull(mGls.getAccount(true));
-
- mGls.saveUsernameAndPassword("vespa@gmail.com", "meow",
- GoogleLoginServiceConstants.FLAG_GOOGLE_ACCOUNT);
- Assert.assertEquals("vespa@gmail.com", mGls.getAccount(false));
- Assert.assertEquals("vespa@gmail.com", mGls.getAccount(true));
-
- mGls.saveUsernameAndPassword("mackerel@hosted.com", "purr",
- GoogleLoginServiceConstants.FLAG_HOSTED_ACCOUNT);
- Assert.assertEquals("mackerel@hosted.com", mGls.getAccount(false));
- Assert.assertEquals("vespa@gmail.com", mGls.getAccount(true));
- }
-
- public void listsEqual(String[] a, String[] b) {
- Assert.assertEquals(a.length, b.length);
- Arrays.sort(a);
- Arrays.sort(b);
- Assert.assertTrue(Arrays.equals(a, b));
- }
-
- public void testAuthTokens() throws Exception {
- Assert.assertNotNull(mGls);
- mGls.deleteAllAccounts();
-
- Assert.assertNull(mGls.peekCredentials("vespa@example.com", "mail"));
-
- mGls.saveUsernameAndPassword("vespa@example.com", "meow",
- GoogleLoginServiceConstants.FLAG_HOSTED_ACCOUNT);
- Assert.assertNull(mGls.peekCredentials("vespa@example.com", "mail"));
- Assert.assertNull(mGls.peekCredentials(null, "mail"));
-
- mGls.saveAuthToken("vespa@example.com", "mail", "1234");
- Assert.assertEquals("1234", mGls.peekCredentials("vespa@example.com", "mail"));
- Assert.assertEquals("1234", mGls.peekCredentials(null, "mail"));
-
- mGls.saveUsernameAndPassword("mackerel@example.com", "purr",
- GoogleLoginServiceConstants.FLAG_GOOGLE_ACCOUNT);
- mGls.saveAuthToken("mackerel@example.com", "mail", "5678");
- Assert.assertEquals("1234", mGls.peekCredentials(null, "mail"));
-
- mGls.saveAuthToken("mackerel@example.com", "mail", "8765");
- Assert.assertEquals("8765", mGls.peekCredentials("mackerel@example.com", "mail"));
-
- GoogleLoginCredentialsResult r = mGls.blockingGetCredentials(
- "vespa@example.com", "mail", false);
- Assert.assertEquals("vespa@example.com", r.getAccount());
- Assert.assertEquals("1234", r.getCredentialsString());
- Assert.assertNull(r.getCredentialsIntent());
-
- mGls.saveAuthToken("vespa@example.com", "cl", "abcd");
- Assert.assertEquals("1234", mGls.peekCredentials("vespa@example.com", "mail"));
- Assert.assertEquals("abcd", mGls.peekCredentials("vespa@example.com", "cl"));
- }
-}
diff --git a/tests/DumpRenderTree/AndroidManifest.xml b/tests/DumpRenderTree/AndroidManifest.xml
index efa41131..03b7e26 100644
--- a/tests/DumpRenderTree/AndroidManifest.xml
+++ b/tests/DumpRenderTree/AndroidManifest.xml
@@ -17,14 +17,16 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.dumprendertree">
<application android:name="HTMLHostApp">
<uses-library android:name="android.test.runner" />
- <activity android:name="Menu" android:label="1 Dump Render Tree">
+ <activity android:name="Menu" android:label="Dump Render Tree"
+ android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.TEST" />
</intent-filter>
</activity>
- <activity android:name="TestShellActivity" android:launchMode="singleTop" />
- <activity android:name="ReliabilityTestActivity" />
+ <activity android:name="TestShellActivity" android:launchMode="singleTop"
+ android:screenOrientation="portrait"/>
+ <activity android:name="ReliabilityTestActivity" android:screenOrientation="portrait"/>
</application>
<instrumentation android:name=".LayoutTestsAutoRunner"
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index ae8f242..c530dd4 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1750,51 +1750,59 @@
depth++;
String8 tag(tree.getElementName(&len));
// printf("Depth %d tag %s\n", depth, tag.string());
+ bool keepTag = false;
if (depth == 1) {
if (tag != "manifest") {
fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
return -1;
}
pkg = getAttribute(tree, NULL, "package", NULL);
- } else if (depth == 2 && tag == "application") {
- inApplication = true;
+ } else if (depth == 2) {
+ if (tag == "application") {
+ inApplication = true;
+ keepTag = true;
+ } else if (tag == "instrumentation") {
+ keepTag = true;
+ }
}
- if (inApplication) {
- if (tag == "application" || tag == "activity" || tag == "service" || tag == "receiver"
- || tag == "provider") {
- String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
- "name", &error);
- if (error != "") {
- fprintf(stderr, "ERROR: %s\n", error.string());
- return -1;
+ if (!keepTag && inApplication && depth == 3) {
+ if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
+ keepTag = true;
+ }
+ }
+ if (keepTag) {
+ String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
+ "name", &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR: %s\n", error.string());
+ return -1;
+ }
+ if (name.length() > 0) {
+ // asdf --> package.asdf
+ // .asdf .a.b --> package.asdf package.a.b
+ // asdf.adsf --> asdf.asdf
+ String8 rule("-keep class ");
+ const char* p = name.string();
+ const char* q = strchr(p, '.');
+ if (p == q) {
+ rule += pkg;
+ rule += name;
+ } else if (q == NULL) {
+ rule += pkg;
+ rule += ".";
+ rule += name;
+ } else {
+ rule += name;
}
- if (name.length() > 0) {
- // asdf --> package.asdf
- // .asdf .a.b --> package.asdf package.a.b
- // asdf.adsf --> asdf.asdf
- String8 rule("-keep class ");
- const char* p = name.string();
- const char* q = strchr(p, '.');
- if (p == q) {
- rule += pkg;
- rule += name;
- } else if (q == NULL) {
- rule += pkg;
- rule += ".";
- rule += name;
- } else {
- rule += name;
- }
- String8 location = tag;
- location += " ";
- location += assFile->getSourceFile();
- char lineno[20];
- sprintf(lineno, ":%d", tree.getLineNumber());
- location += lineno;
+ String8 location = tag;
+ location += " ";
+ location += assFile->getSourceFile();
+ char lineno[20];
+ sprintf(lineno, ":%d", tree.getLineNumber());
+ location += lineno;
- keep->add(rule, location);
- }
+ keep->add(rule, location);
}
}
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
index c49e11e..8bf5e85 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
@@ -31,6 +31,7 @@
import android.graphics.Region.Op;
import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
@@ -103,13 +104,37 @@
* Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
* <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
*/
- private Graphics2D getNewGraphics(Paint paint, Graphics2D g) {
+ private Graphics2D getCustomGraphics(Paint paint) {
// make new one
+ Graphics2D g = getGraphics2d();
g = (Graphics2D)g.create();
+
+ // configure it
g.setColor(new Color(paint.getColor()));
int alpha = paint.getAlpha();
float falpha = alpha / 255.f;
+ Style style = paint.getStyle();
+ if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
+ PathEffect e = paint.getPathEffect();
+ if (e instanceof DashPathEffect) {
+ DashPathEffect dpe = (DashPathEffect)e;
+ g.setStroke(new BasicStroke(
+ paint.getStrokeWidth(),
+ paint.getStrokeCap().getJavaCap(),
+ paint.getStrokeJoin().getJavaJoin(),
+ paint.getStrokeMiter(),
+ dpe.getIntervals(),
+ dpe.getPhase()));
+ } else {
+ g.setStroke(new BasicStroke(
+ paint.getStrokeWidth(),
+ paint.getStrokeCap().getJavaCap(),
+ paint.getStrokeJoin().getJavaJoin(),
+ paint.getStrokeMiter()));
+ }
+ }
+
Xfermode xfermode = paint.getXfermode();
if (xfermode instanceof PorterDuffXfermode) {
PorterDuff.Mode mode = ((PorterDuffXfermode)xfermode).getMode();
@@ -783,11 +808,9 @@
}
private final void doDrawRect(int left, int top, int width, int height, Paint paint) {
- // get current graphisc
- if (width != 0 && height != 0) {
- Graphics2D g = getGraphics2d();
-
- g = getNewGraphics(paint, g);
+ if (width > 0 && height > 0) {
+ // get a Graphics2D object configured with the drawing parameters.
+ Graphics2D g = getCustomGraphics(paint);
Style style = paint.getStyle();
@@ -810,11 +833,9 @@
*/
@Override
public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
- // get current graphisc
- if (rect.width() != 0 && rect.height() != 0) {
- Graphics2D g = getGraphics2d();
-
- g = getNewGraphics(paint, g);
+ if (rect.width() > 0 && rect.height() > 0) {
+ // get a Graphics2D object configured with the drawing parameters.
+ Graphics2D g = getCustomGraphics(paint);
Style style = paint.getStyle();
@@ -844,10 +865,8 @@
*/
@Override
public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
- // get current graphisc
- Graphics2D g = getGraphics2d();
-
- g = getNewGraphics(paint, g);
+ // get a Graphics2D object configured with the drawing parameters.
+ Graphics2D g = getCustomGraphics(paint);
g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
@@ -860,10 +879,8 @@
*/
@Override
public void drawLines(float[] pts, int offset, int count, Paint paint) {
- // get current graphisc
- Graphics2D g = getGraphics2d();
-
- g = getNewGraphics(paint, g);
+ // get a Graphics2D object configured with the drawing parameters.
+ Graphics2D g = getCustomGraphics(paint);
for (int i = 0 ; i < count ; i += 4) {
g.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
@@ -887,10 +904,8 @@
*/
@Override
public void drawCircle(float cx, float cy, float radius, Paint paint) {
- // get current graphisc
- Graphics2D g = getGraphics2d();
-
- g = getNewGraphics(paint, g);
+ // get a Graphics2D object configured with the drawing parameters.
+ Graphics2D g = getCustomGraphics(paint);
Style style = paint.getStyle();
@@ -914,10 +929,8 @@
*/
@Override
public void drawOval(RectF oval, Paint paint) {
- // get current graphics
- Graphics2D g = getGraphics2d();
-
- g = getNewGraphics(paint, g);
+ // get a Graphics2D object configured with the drawing parameters.
+ Graphics2D g = getCustomGraphics(paint);
Style style = paint.getStyle();
@@ -939,10 +952,8 @@
*/
@Override
public void drawPath(Path path, Paint paint) {
- // get current graphics
- Graphics2D g = getGraphics2d();
-
- g = getNewGraphics(paint, g);
+ // get a Graphics2D object configured with the drawing parameters.
+ Graphics2D g = getCustomGraphics(paint);
Style style = paint.getStyle();
diff --git a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java
new file mode 100644
index 0000000..46d4c70
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 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 android.graphics;
+
+public class DashPathEffect extends PathEffect {
+
+ private final float[] mIntervals;
+ private final float mPhase;
+
+ /**
+ * The intervals array must contain an even number of entries (>=2), with
+ * the even indices specifying the "on" intervals, and the odd indices
+ * specifying the "off" intervals. phase is an offset into the intervals
+ * array (mod the sum of all of the intervals). The intervals array
+ * controls the length of the dashes. The paint's strokeWidth controls the
+ * thickness of the dashes.
+ * Note: this patheffect only affects drawing with the paint's style is set
+ * to STROKE or STROKE_AND_FILL. It is ignored if the drawing is done with
+ * style == FILL.
+ * @param intervals array of ON and OFF distances
+ * @param phase offset into the intervals array
+ */
+ public DashPathEffect(float intervals[], float phase) {
+ if (intervals.length < 2) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+
+ mIntervals = intervals;
+ mPhase = phase;
+ }
+
+ public float[] getIntervals() {
+ return mIntervals;
+ }
+
+ public float getPhase() {
+ return mPhase;
+ }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/GradientShader.java b/tools/layoutlib/bridge/src/android/graphics/GradientShader.java
new file mode 100644
index 0000000..8c5a2a4
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/GradientShader.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2010 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 android.graphics;
+
+
+/**
+ * Base class for Gradient shader. This is not a standard android class and is just used
+ * as a base class for the re-implemented gradient classes.
+ *
+ * It also provides a base class to handle common code between the different shaders'
+ * implementations of {@link java.awt.Paint}.
+ *
+ * @see LinearGradient
+ * @see RadialGradient
+ * @see SweepGradient
+ */
+public abstract class GradientShader extends Shader {
+
+ protected final int[] mColors;
+ protected final float[] mPositions;
+
+ /**
+ * Creates the base shader and do some basic test on the parameters.
+ *
+ * @param colors The colors to be distributed along the gradient line
+ * @param positions May be null. The relative positions [0..1] of each
+ * corresponding color in the colors array. If this is null, the
+ * the colors are distributed evenly along the gradient line.
+ */
+ protected GradientShader(int colors[], float positions[]) {
+ if (colors.length < 2) {
+ throw new IllegalArgumentException("needs >= 2 number of colors");
+ }
+ if (positions != null && colors.length != positions.length) {
+ throw new IllegalArgumentException("color and position arrays must be of equal length");
+ }
+
+ if (positions == null) {
+ float spacing = 1.f / (colors.length - 1);
+ positions = new float[colors.length];
+ positions[0] = 0.f;
+ positions[colors.length-1] = 1.f;
+ for (int i = 1; i < colors.length - 1 ; i++) {
+ positions[i] = spacing * i;
+ }
+ }
+
+ mColors = colors;
+ mPositions = positions;
+ }
+
+ /**
+ * Base class for (Java) Gradient Paints. This handles computing the gradient colors based
+ * on the color and position lists, as well as the {@link TileMode}
+ *
+ */
+ protected abstract static class GradientPaint implements java.awt.Paint {
+ private final static int GRADIENT_SIZE = 100;
+
+ private final int[] mColors;
+ private final float[] mPositions;
+ private final TileMode mTileMode;
+ private int[] mGradient;
+
+ protected GradientPaint(int[] colors, float[] positions, TileMode tileMode) {
+ mColors = colors;
+ mPositions = positions;
+ mTileMode = tileMode;
+ }
+
+ public int getTransparency() {
+ return java.awt.Paint.TRANSLUCENT;
+ }
+
+ /**
+ * Pre-computes the colors for the gradient. This must be called once before any call
+ * to {@link #getGradientColor(float)}
+ */
+ protected synchronized void precomputeGradientColors() {
+ if (mGradient == null) {
+ // actually create an array with an extra size, so that we can really go
+ // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0
+ mGradient = new int[GRADIENT_SIZE+1];
+
+ int prevPos = 0;
+ int nextPos = 1;
+ for (int i = 0 ; i <= GRADIENT_SIZE ; i++) {
+ // compute current position
+ float currentPos = (float)i/GRADIENT_SIZE;
+ while (currentPos > mPositions[nextPos]) {
+ prevPos = nextPos++;
+ }
+
+ float percent = (currentPos - mPositions[prevPos]) /
+ (mPositions[nextPos] - mPositions[prevPos]);
+
+ mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent);
+ }
+ }
+ }
+
+ /**
+ * Returns the color based on the position in the gradient.
+ * <var>pos</var> can be anything, even < 0 or > > 1, as the gradient
+ * will use {@link TileMode} value to convert it into a [0,1] value.
+ */
+ protected int getGradientColor(float pos) {
+ if (pos < 0.f) {
+ if (mTileMode != null) {
+ switch (mTileMode) {
+ case CLAMP:
+ pos = 0.f;
+ break;
+ case REPEAT:
+ // remove the integer part to stay in the [0,1] range
+ // careful: this is a negative value, so use ceil instead of floor
+ pos = pos - (float)Math.ceil(pos);
+ break;
+ case MIRROR:
+ // get the integer and the decimal part
+ // careful: this is a negative value, so use ceil instead of floor
+ int intPart = (int)Math.ceil(pos);
+ pos = pos - intPart;
+ // 0 -> -1 : mirrored order
+ // -1 -> -2: normal order
+ // etc..
+ // this means if the intpart is even we invert
+ if ((intPart % 2) == 0) {
+ pos = 1.f - pos;
+ }
+ break;
+ }
+ } else {
+ pos = 0.0f;
+ }
+ } else if (pos > 1f) {
+ if (mTileMode != null) {
+ switch (mTileMode) {
+ case CLAMP:
+ pos = 1.f;
+ break;
+ case REPEAT:
+ // remove the integer part to stay in the [0,1] range
+ pos = pos - (float)Math.floor(pos);
+ break;
+ case MIRROR:
+ // get the integer and the decimal part
+ int intPart = (int)Math.floor(pos);
+ pos = pos - intPart;
+ // 0 -> 1 : normal order
+ // 1 -> 2: mirrored
+ // etc..
+ // this means if the intpart is odd we invert
+ if ((intPart % 2) == 1) {
+ pos = 1.f - pos;
+ }
+ break;
+ }
+ } else {
+ pos = 1.0f;
+ }
+ }
+
+ int index = (int)((pos * GRADIENT_SIZE) + .5);
+
+ return mGradient[index];
+ }
+
+ /**
+ * Returns the color between c1, and c2, based on the percent of the distance
+ * between c1 and c2.
+ */
+ private int computeColor(int c1, int c2, float percent) {
+ int a = computeChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent);
+ int r = computeChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent);
+ int g = computeChannel((c1 >> 8) & 0xFF, (c2 >> 8) & 0xFF, percent);
+ int b = computeChannel((c1 ) & 0xFF, (c2 ) & 0xFF, percent);
+ return a << 24 | r << 16 | g << 8 | b;
+ }
+
+ /**
+ * Returns the channel value between 2 values based on the percent of the distance between
+ * the 2 values..
+ */
+ private int computeChannel(int c1, int c2, float percent) {
+ return c1 + (int)((percent * (c2-c1)) + .5);
+ }
+
+
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
index 38ffed3..10c4a5e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
@@ -16,19 +16,9 @@
package android.graphics;
-import java.awt.Paint;
-import java.awt.PaintContext;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
-import java.awt.image.ColorModel;
-import java.awt.image.Raster;
+public class LinearGradient extends GradientShader {
-public class LinearGradient extends Shader {
-
- private Paint mJavaPaint;
+ private java.awt.Paint mJavaPaint;
/**
* Create a shader that draws a linear gradient along a line.
@@ -45,24 +35,8 @@
*/
public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
TileMode tile) {
- if (colors.length < 2) {
- throw new IllegalArgumentException("needs >= 2 number of colors");
- }
- if (positions != null && colors.length != positions.length) {
- throw new IllegalArgumentException("color and position arrays must be of equal length");
- }
-
- if (positions == null) {
- float spacing = 1.f / (colors.length - 1);
- positions = new float[colors.length];
- positions[0] = 0.f;
- positions[colors.length-1] = 1.f;
- for (int i = 1; i < colors.length - 1 ; i++) {
- positions[i] = spacing * i;
- }
- }
-
- mJavaPaint = new MultiPointLinearGradientPaint(x0, y0, x1, y1, colors, positions, tile);
+ super(colors, positions);
+ mJavaPaint = new LinearGradientPaint(x0, y0, x1, y1, mColors, mPositions, tile);
}
/**
@@ -84,117 +58,62 @@
// ---------- Custom Methods
@Override
- public Paint getJavaPaint() {
+ java.awt.Paint getJavaPaint() {
return mJavaPaint;
}
- private static class MultiPointLinearGradientPaint implements Paint {
- private final static int GRADIENT_SIZE = 100;
+ /**
+ * Linear Gradient (Java) Paint able to handle more than 2 points, as
+ * {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile
+ * modes.
+ */
+ private static class LinearGradientPaint extends GradientPaint {
private final float mX0;
private final float mY0;
private final float mDx;
private final float mDy;
private final float mDSize2;
- private final int[] mColors;
- private final float[] mPositions;
- private final TileMode mTile;
- private int[] mGradient;
- public MultiPointLinearGradientPaint(float x0, float y0, float x1, float y1, int colors[],
+ public LinearGradientPaint(float x0, float y0, float x1, float y1, int colors[],
float positions[], TileMode tile) {
+ super(colors, positions, tile);
mX0 = x0;
mY0 = y0;
mDx = x1 - x0;
mDy = y1 - y0;
mDSize2 = mDx * mDx + mDy * mDy;
-
- mColors = colors;
- mPositions = positions;
- mTile = tile;
}
- public PaintContext createContext(ColorModel cm, Rectangle deviceBounds,
- Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
- prepareColors();
- return new MultiPointLinearGradientPaintContext(cm, deviceBounds,
- userBounds, xform, hints);
+ public java.awt.PaintContext createContext(
+ java.awt.image.ColorModel colorModel,
+ java.awt.Rectangle deviceBounds,
+ java.awt.geom.Rectangle2D userBounds,
+ java.awt.geom.AffineTransform xform,
+ java.awt.RenderingHints hints) {
+ precomputeGradientColors();
+ return new LinearGradientPaintContext(colorModel);
}
- public int getTransparency() {
- return TRANSLUCENT;
- }
+ private class LinearGradientPaintContext implements java.awt.PaintContext {
- private synchronized void prepareColors() {
- if (mGradient == null) {
- // actually create an array with an extra size, so that we can really go
- // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0
- mGradient = new int[GRADIENT_SIZE+1];
+ private final java.awt.image.ColorModel mColorModel;
- int prevPos = 0;
- int nextPos = 1;
- for (int i = 0 ; i <= GRADIENT_SIZE ; i++) {
- // compute current position
- float currentPos = (float)i/GRADIENT_SIZE;
- while (currentPos > mPositions[nextPos]) {
- prevPos = nextPos++;
- }
-
- float percent = (currentPos - mPositions[prevPos]) /
- (mPositions[nextPos] - mPositions[prevPos]);
-
- mGradient[i] = getColor(mColors[prevPos], mColors[nextPos], percent);
- }
- }
- }
-
- /**
- * Returns the color between c1, and c2, based on the percent of the distance
- * between c1 and c2.
- */
- private int getColor(int c1, int c2, float percent) {
- int a = getChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent);
- int r = getChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent);
- int g = getChannel((c1 >> 8) & 0xFF, (c2 >> 8) & 0xFF, percent);
- int b = getChannel((c1 ) & 0xFF, (c2 ) & 0xFF, percent);
- return a << 24 | r << 16 | g << 8 | b;
- }
-
- /**
- * Returns the channel value between 2 values based on the percent of the distance between
- * the 2 values..
- */
- private int getChannel(int c1, int c2, float percent) {
- return c1 + (int)((percent * (c2-c1)) + .5);
- }
-
- private class MultiPointLinearGradientPaintContext implements PaintContext {
-
- private ColorModel mColorModel;
- private final Rectangle mDeviceBounds;
- private final Rectangle2D mUserBounds;
- private final AffineTransform mXform;
- private final RenderingHints mHints;
-
- public MultiPointLinearGradientPaintContext(ColorModel cm, Rectangle deviceBounds,
- Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
- mColorModel = cm;
+ public LinearGradientPaintContext(java.awt.image.ColorModel colorModel) {
+ mColorModel = colorModel;
// FIXME: so far all this is always the same rect gotten in getRaster with an indentity matrix?
- mDeviceBounds = deviceBounds;
- mUserBounds = userBounds;
- mXform = xform;
- mHints = hints;
}
public void dispose() {
}
- public ColorModel getColorModel() {
+ public java.awt.image.ColorModel getColorModel() {
return mColorModel;
}
- public Raster getRaster(int x, int y, int w, int h) {
- BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+ public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+ java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+ java.awt.image.BufferedImage.TYPE_INT_ARGB);
int[] data = new int[w*h];
@@ -236,7 +155,7 @@
private int getColor(float absPos, float refPos, float refSize) {
float pos = (absPos - refPos) / refSize;
- return getIndexFromPos(pos);
+ return getGradientColor(pos);
}
/**
@@ -248,66 +167,7 @@
// from it get the position relative to the vector
float pos = (float) ((_x - mX0) / mDx);
- return getIndexFromPos(pos);
- }
-
- /**
- * Returns the color based on the position in the gradient.
- * <var>pos</var> can be anything, even < 0 or > > 1, as the gradient
- * will use {@link TileMode} value to convert it into a [0,1] value.
- */
- private int getIndexFromPos(float pos) {
- if (pos < 0.f) {
- switch (mTile) {
- case CLAMP:
- pos = 0.f;
- break;
- case REPEAT:
- // remove the integer part to stay in the [0,1] range
- // careful: this is a negative value, so use ceil instead of floor
- pos = pos - (float)Math.ceil(pos);
- break;
- case MIRROR:
- // get the integer and the decimal part
- // careful: this is a negative value, so use ceil instead of floor
- int intPart = (int)Math.ceil(pos);
- pos = pos - intPart;
- // 0 -> -1 : mirrored order
- // -1 -> -2: normal order
- // etc..
- // this means if the intpart is even we invert
- if ((intPart % 2) == 0) {
- pos = 1.f - pos;
- }
- break;
- }
- } else if (pos > 1f) {
- switch (mTile) {
- case CLAMP:
- pos = 1.f;
- break;
- case REPEAT:
- // remove the integer part to stay in the [0,1] range
- pos = pos - (float)Math.floor(pos);
- break;
- case MIRROR:
- // get the integer and the decimal part
- int intPart = (int)Math.floor(pos);
- pos = pos - intPart;
- // 0 -> 1 : normal order
- // 1 -> 2: mirrored
- // etc..
- // this means if the intpart is odd we invert
- if ((intPart % 2) == 1) {
- pos = 1.f - pos;
- }
- break;
- }
- }
-
- int index = (int)((pos * GRADIENT_SIZE) + .5);
-
- return mGradient[index];
+ return getGradientColor(pos);
}
}
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java
index 312dab3..2d03618 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java
@@ -21,6 +21,7 @@
import android.text.SpannedString;
import android.text.TextUtils;
+import java.awt.BasicStroke;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.font.FontRenderContext;
@@ -127,6 +128,19 @@
this.nativeInt = nativeInt;
}
final int nativeInt;
+
+ /** custom for layoutlib */
+ public int getJavaCap() {
+ switch (this) {
+ case BUTT:
+ return BasicStroke.CAP_BUTT;
+ case ROUND:
+ return BasicStroke.CAP_ROUND;
+ default:
+ case SQUARE:
+ return BasicStroke.CAP_SQUARE;
+ }
+ }
}
/**
@@ -151,6 +165,19 @@
this.nativeInt = nativeInt;
}
final int nativeInt;
+
+ /** custom for layoutlib */
+ public int getJavaJoin() {
+ switch (this) {
+ default:
+ case MITER:
+ return BasicStroke.JOIN_MITER;
+ case ROUND:
+ return BasicStroke.JOIN_ROUND;
+ case BEVEL:
+ return BasicStroke.JOIN_BEVEL;
+ }
+ }
}
/**
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
index 13848c5..4409a80 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
@@ -16,55 +16,117 @@
package android.graphics;
-import java.awt.Paint;
+public class RadialGradient extends GradientShader {
-public class RadialGradient extends Shader {
+ private RadialGradientPaint mPaint;
- /** Create a shader that draws a radial gradient given the center and radius.
- @param x The x-coordinate of the center of the radius
- @param y The y-coordinate of the center of the radius
- @param radius Must be positive. The radius of the circle for this gradient
- @param colors The colors to be distributed between the center and edge of the circle
- @param positions May be NULL. The relative position of
- each corresponding color in the colors array. If this is NULL,
- the the colors are distributed evenly between the center and edge of the circle.
- @param tile The Shader tiling mode
- */
- public RadialGradient(float x, float y, float radius,
- int colors[], float positions[], TileMode tile) {
- if (radius <= 0) {
- throw new IllegalArgumentException("radius must be > 0");
- }
- if (colors.length < 2) {
- throw new IllegalArgumentException("needs >= 2 number of colors");
- }
- if (positions != null && colors.length != positions.length) {
- throw new IllegalArgumentException("color and position arrays must be of equal length");
- }
+ /**
+ * Create a shader that draws a radial gradient given the center and radius.
+ *
+ * @param x The x-coordinate of the center of the radius
+ * @param y The y-coordinate of the center of the radius
+ * @param radius Must be positive. The radius of the circle for this
+ * gradient
+ * @param colors The colors to be distributed between the center and edge of
+ * the circle
+ * @param positions May be NULL. The relative position of each corresponding
+ * color in the colors array. If this is NULL, the the colors are
+ * distributed evenly between the center and edge of the circle.
+ * @param tile The Shader tiling mode
+ */
+ public RadialGradient(float x, float y, float radius, int colors[], float positions[],
+ TileMode tile) {
+ super(colors, positions);
+ if (radius <= 0) {
+ throw new IllegalArgumentException("radius must be > 0");
+ }
- // FIXME Implement shader
- }
+ mPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile);
+ }
- /** Create a shader that draws a radial gradient given the center and radius.
- @param x The x-coordinate of the center of the radius
- @param y The y-coordinate of the center of the radius
- @param radius Must be positive. The radius of the circle for this gradient
- @param color0 The color at the center of the circle.
- @param color1 The color at the edge of the circle.
- @param tile The Shader tiling mode
- */
- public RadialGradient(float x, float y, float radius,
- int color0, int color1, TileMode tile) {
- if (radius <= 0) {
- throw new IllegalArgumentException("radius must be > 0");
- }
- // FIXME Implement shader
- }
+ /**
+ * Create a shader that draws a radial gradient given the center and radius.
+ *
+ * @param x The x-coordinate of the center of the radius
+ * @param y The y-coordinate of the center of the radius
+ * @param radius Must be positive. The radius of the circle for this
+ * gradient
+ * @param color0 The color at the center of the circle.
+ * @param color1 The color at the edge of the circle.
+ * @param tile The Shader tiling mode
+ */
+ public RadialGradient(float x, float y, float radius, int color0, int color1, TileMode tile) {
+ this(x, y, radius, new int[] { color0, color1 }, null /* positions */, tile);
+ }
@Override
- Paint getJavaPaint() {
- // TODO Auto-generated method stub
- return null;
+ java.awt.Paint getJavaPaint() {
+ return mPaint;
}
-}
+ private static class RadialGradientPaint extends GradientPaint {
+
+ private final float mX;
+ private final float mY;
+ private final float mRadius;
+
+ public RadialGradientPaint(float x, float y, float radius, int[] colors, float[] positions, TileMode mode) {
+ super(colors, positions, mode);
+ mX = x;
+ mY = y;
+ mRadius = radius;
+ }
+
+ public java.awt.PaintContext createContext(
+ java.awt.image.ColorModel colorModel,
+ java.awt.Rectangle deviceBounds,
+ java.awt.geom.Rectangle2D userBounds,
+ java.awt.geom.AffineTransform xform,
+ java.awt.RenderingHints hints) {
+ precomputeGradientColors();
+ return new RadialGradientPaintContext(colorModel);
+ }
+
+ private class RadialGradientPaintContext implements java.awt.PaintContext {
+
+ private final java.awt.image.ColorModel mColorModel;
+
+ public RadialGradientPaintContext(java.awt.image.ColorModel colorModel) {
+ mColorModel = colorModel;
+ }
+
+ public void dispose() {
+ }
+
+ public java.awt.image.ColorModel getColorModel() {
+ return mColorModel;
+ }
+
+ public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+ java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+ java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+ int[] data = new int[w*h];
+
+ // compute distance from each point to the center, and figure out the distance from
+ // it.
+ int index = 0;
+ for (int iy = 0 ; iy < h ; iy++) {
+ for (int ix = 0 ; ix < w ; ix++) {
+ float _x = x + ix - mX;
+ float _y = y + iy - mY;
+ float distance = (float) Math.sqrt(_x * _x + _y * _y);
+
+ data[index++] = getGradientColor(distance / mRadius);
+ }
+ }
+
+ image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+ return image.getRaster();
+ }
+
+ }
+ }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
index 21d8244..87036ed 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
@@ -16,9 +16,9 @@
package android.graphics;
-import java.awt.Paint;
+public class SweepGradient extends GradientShader {
-public class SweepGradient extends Shader {
+ private SweepGradientPaint mPaint;
/**
* A subclass of Shader that draws a sweep gradient around a center point.
@@ -36,15 +36,9 @@
*/
public SweepGradient(float cx, float cy,
int colors[], float positions[]) {
- if (colors.length < 2) {
- throw new IllegalArgumentException("needs >= 2 number of colors");
- }
- if (positions != null && colors.length != positions.length) {
- throw new IllegalArgumentException(
- "color and position arrays must be of equal length");
- }
+ super(colors, positions);
- // FIXME Implement shader
+ mPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
}
/**
@@ -56,13 +50,91 @@
* @param color1 The color to use at the end of the sweep
*/
public SweepGradient(float cx, float cy, int color0, int color1) {
- // FIXME Implement shader
+ this(cx, cy, new int[] { color0, color1}, null /*positions*/);
}
@Override
- Paint getJavaPaint() {
- // TODO Auto-generated method stub
- return null;
+ java.awt.Paint getJavaPaint() {
+ return mPaint;
}
+
+ private static class SweepGradientPaint extends GradientPaint {
+
+ private final float mCx;
+ private final float mCy;
+
+ public SweepGradientPaint(float cx, float cy, int[] colors, float[] positions) {
+ super(colors, positions, null /*tileMode*/);
+ mCx = cx;
+ mCy = cy;
+ }
+
+ public java.awt.PaintContext createContext(
+ java.awt.image.ColorModel colorModel,
+ java.awt.Rectangle deviceBounds,
+ java.awt.geom.Rectangle2D userBounds,
+ java.awt.geom.AffineTransform xform,
+ java.awt.RenderingHints hints) {
+ precomputeGradientColors();
+ return new SweepGradientPaintContext(colorModel);
+ }
+
+ private class SweepGradientPaintContext implements java.awt.PaintContext {
+
+ private final java.awt.image.ColorModel mColorModel;
+
+ public SweepGradientPaintContext(java.awt.image.ColorModel colorModel) {
+ mColorModel = colorModel;
+ }
+
+ public void dispose() {
+ }
+
+ public java.awt.image.ColorModel getColorModel() {
+ return mColorModel;
+ }
+
+ public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+ java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+ java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+ int[] data = new int[w*h];
+
+ // compute angle from each point to the center, and figure out the distance from
+ // it.
+ int index = 0;
+ for (int iy = 0 ; iy < h ; iy++) {
+ for (int ix = 0 ; ix < w ; ix++) {
+ float dx = x + ix - mCx;
+ float dy = y + iy - mCy;
+ float angle;
+ if (dx == 0) {
+ angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);
+ } else if (dy == 0) {
+ angle = (float) (dx < 0 ? Math.PI : 0);
+ } else {
+ angle = (float) Math.atan(dy / dx);
+ if (dx > 0) {
+ if (dy < 0) {
+ angle += Math.PI * 2;
+ }
+ } else {
+ angle += Math.PI;
+ }
+ }
+
+ // convert to 0-1. value and get color
+ data[index++] = getGradientColor((float) (angle / (2 * Math.PI)));
+ }
+ }
+
+ image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+ return image.getRaster();
+ }
+
+ }
+ }
+
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 2623570..2ed8641 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -47,6 +47,7 @@
"android.graphics.BitmapShader", "android.graphics._Original_BitmapShader",
"android.graphics.Canvas", "android.graphics._Original_Canvas",
"android.graphics.ComposeShader", "android.graphics._Original_ComposeShader",
+ "android.graphics.DashPathEffect", "android.graphics._Original_DashPathEffect",
"android.graphics.LinearGradient", "android.graphics._Original_LinearGradient",
"android.graphics.Matrix", "android.graphics._Original_Matrix",
"android.graphics.Paint", "android.graphics._Original_Paint",
diff --git a/tools/preload/Policy.java b/tools/preload/Policy.java
index 7a190ac..a8d761d 100644
--- a/tools/preload/Policy.java
+++ b/tools/preload/Policy.java
@@ -46,7 +46,6 @@
"com.android.phone",
"com.google.android.apps.maps.FriendService",
"com.google.android.apps.maps.LocationFriendService",
- "com.google.android.googleapps",
"com.google.process.gapps",
"android.tts"
));