summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camera/libcameraservice/CameraService.cpp2
-rw-r--r--camera/libcameraservice/CameraService.h1
-rw-r--r--core/java/android/app/ListActivity.java2
-rw-r--r--core/java/android/content/ContentProvider.java179
-rw-r--r--core/java/android/content/res/ColorStateList.java4
-rw-r--r--core/java/android/database/sqlite/SQLiteOpenHelper.java39
-rw-r--r--core/java/android/hardware/Camera.java447
-rw-r--r--core/java/android/net/SSLCertificateSocketFactory.java142
-rw-r--r--core/java/android/os/DropBoxManager.java89
-rw-r--r--core/java/android/os/IPowerManager.aidl1
-rw-r--r--core/java/android/os/Parcel.java19
-rw-r--r--core/java/android/os/ParcelFileDescriptor.java5
-rw-r--r--core/java/android/view/View.java4
-rw-r--r--core/java/android/webkit/WebView.java12
-rw-r--r--core/java/android/widget/BaseExpandableListAdapter.java4
-rw-r--r--core/java/android/widget/HeterogeneousExpandableList.java27
-rw-r--r--core/java/com/google/android/mms/ContentType.java1
-rw-r--r--core/java/com/google/android/mms/pdu/PduParser.java4
-rw-r--r--core/java/com/google/android/mms/pdu/PduPersister.java7
-rw-r--r--core/jni/android_bluetooth_HeadsetBase.cpp4
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java43
-rw-r--r--core/tests/coretests/AndroidManifest.xml2
-rw-r--r--core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java440
-rw-r--r--docs/html/guide/developing/eclipse-adt.jd7
-rw-r--r--docs/html/guide/developing/other-ide.jd5
-rw-r--r--docs/html/guide/developing/tools/avd.jd269
-rw-r--r--docs/html/guide/developing/tools/bmgr.jd13
-rw-r--r--docs/html/guide/guide_toc.cs3
-rw-r--r--docs/html/guide/practices/ui_guidelines/activity_task_design.jd4
-rw-r--r--docs/html/guide/publishing/licensing.jd2376
-rw-r--r--docs/html/guide/publishing/preparing.jd31
-rw-r--r--docs/html/guide/publishing/publishing.jd25
-rw-r--r--docs/html/guide/samples/index.jd98
-rw-r--r--docs/html/guide/topics/data/backup.jd74
-rw-r--r--docs/html/guide/topics/resources/available-resources.jd17
-rw-r--r--docs/html/guide/topics/resources/color-list-resource.jd1
-rw-r--r--docs/html/guide/topics/resources/drawable-resource.jd1323
-rw-r--r--docs/html/guide/topics/resources/layout-resource.jd81
-rw-r--r--docs/html/guide/topics/resources/menu-resource.jd12
-rw-r--r--docs/html/guide/topics/resources/more-resources.jd128
-rw-r--r--docs/html/guide/topics/resources/providing-resources.jd13
-rw-r--r--docs/html/guide/topics/search/adding-custom-suggestions.jd589
-rw-r--r--docs/html/guide/topics/search/adding-recent-query-suggestions.jd214
-rw-r--r--docs/html/guide/topics/search/index.jd106
-rw-r--r--docs/html/guide/topics/search/search-dialog.jd460
-rw-r--r--docs/html/guide/topics/search/searchable-config.jd268
-rw-r--r--docs/html/guide/topics/ui/menus.jd892
-rwxr-xr-xdocs/html/images/developing/avd-dialog.pngbin0 -> 86111 bytes
-rw-r--r--docs/html/images/licensing_add_library.pngbin0 -> 70285 bytes
-rw-r--r--docs/html/images/licensing_arch.pngbin0 -> 41227 bytes
-rw-r--r--docs/html/images/licensing_device_signin.pngbin0 -> 75152 bytes
-rw-r--r--docs/html/images/licensing_flow.pngbin0 -> 47151 bytes
-rw-r--r--docs/html/images/licensing_gapis_8.pngbin0 -> 84597 bytes
-rw-r--r--docs/html/images/licensing_package.pngbin0 -> 78821 bytes
-rw-r--r--docs/html/images/licensing_public_key.pngbin0 -> 95876 bytes
-rw-r--r--docs/html/images/licensing_test_response.pngbin0 -> 120461 bytes
-rw-r--r--docs/html/images/resources/clip.pngbin0 -> 2464 bytes
-rw-r--r--docs/html/images/resources/layers.pngbin0 -> 8248 bytes
-rw-r--r--docs/html/index.jd11
-rw-r--r--docs/html/license.jd2
-rw-r--r--docs/html/resources/dashboard/platform-versions.jd76
-rw-r--r--docs/html/resources/dashboard/screens.jd14
-rw-r--r--docs/html/resources/faq/commontasks.jd2
-rw-r--r--docs/html/resources/tutorials/notepad/codelab/NotepadCodeLab.zipbin88600 -> 93415 bytes
-rw-r--r--docs/html/resources/tutorials/notepad/notepad-ex2.jd6
-rw-r--r--docs/html/sdk/android-1.5.jd2
-rw-r--r--docs/html/sdk/android-1.6.jd2
-rw-r--r--docs/html/sdk/android-2.1.jd2
-rw-r--r--docs/html/sdk/android-2.2.jd26
-rw-r--r--docs/html/sdk/download.jd78
-rw-r--r--docs/html/sdk/eclipse-adt.jd5
-rw-r--r--docs/html/sdk/ndk/index.jd29
-rw-r--r--docs/html/sdk/older_releases.jd42
-rw-r--r--docs/html/sdk/requirements.jd3
-rw-r--r--docs/html/sdk/sdk_toc.cs7
-rw-r--r--docs/html/sdk/terms_body.html204
-rw-r--r--docs/html/sitemap.txt1
-rw-r--r--graphics/java/android/graphics/drawable/AnimationDrawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/BitmapDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/ClipDrawable.java9
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java7
-rw-r--r--graphics/java/android/graphics/drawable/GradientDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/InsetDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/LayerDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/LevelListDrawable.java5
-rw-r--r--graphics/java/android/graphics/drawable/RotateDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/ScaleDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/ShapeDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/StateListDrawable.java4
-rw-r--r--graphics/java/android/graphics/drawable/TransitionDrawable.java8
-rw-r--r--include/camera/Camera.h1
-rw-r--r--include/media/IMediaRecorder.h2
-rw-r--r--include/media/PVMediaRecorder.h2
-rw-r--r--include/media/stagefright/AudioPlayer.h4
-rw-r--r--libs/camera/Camera.cpp1
-rw-r--r--media/java/android/media/AudioFormat.java2
-rw-r--r--media/java/android/media/AudioService.java15
-rw-r--r--media/libmedia/IMediaRecorder.cpp2
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.cpp2
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.h2
-rw-r--r--media/libstagefright/AudioPlayer.cpp66
-rw-r--r--media/libstagefright/codecs/aacdec/AACDecoder.cpp110
-rw-r--r--media/libstagefright/include/AACDecoder.h4
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java19
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java9
-rw-r--r--services/java/com/android/server/BackupManagerService.java2
-rw-r--r--services/java/com/android/server/PackageManagerService.java3
-rw-r--r--services/java/com/android/server/PowerManagerService.java110
-rw-r--r--services/tests/servicestests/src/com/android/server/DropBoxTest.java208
-rw-r--r--telephony/java/android/telephony/PhoneNumberUtils.java4
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java19
-rw-r--r--telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl5
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneSubInfo.java21
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java7
114 files changed, 7272 insertions, 2411 deletions
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index 00bd54e..118249e 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -1,7 +1,6 @@
/*
**
** Copyright (C) 2008, The Android Open Source Project
-** Copyright (C) 2008 HTC Inc.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -935,6 +934,7 @@ void CameraService::Client::handleShutter(
mHardware->getRawHeap());
mSurface->registerBuffers(buffers);
+ IPCThreadState::self()->flushCommands();
}
}
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index bc49b1d..75e96c6 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -1,7 +1,6 @@
/*
**
** Copyright (C) 2008, The Android Open Source Project
-** Copyright (C) 2008 HTC Inc.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
diff --git a/core/java/android/app/ListActivity.java b/core/java/android/app/ListActivity.java
index 84a57b5..0a80207 100644
--- a/core/java/android/app/ListActivity.java
+++ b/core/java/android/app/ListActivity.java
@@ -68,7 +68,7 @@ import android.widget.ListView;
* android:layout_weight="1"
* android:drawSelectorOnTop="false"/>
*
- * <TextView id="@id/android:empty"
+ * <TextView android:id="@id/android:empty"
* android:layout_width="match_parent"
* android:layout_height="match_parent"
* android:background="#FF0000"
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 5fb2aae..9b9f796 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -57,6 +57,7 @@ import java.util.ArrayList;
*
* <p>The primary methods that need to be implemented are:
* <ul>
+ * <li>{@link #onCreate} which is called to initialize the provider</li>
* <li>{@link #query} which returns data to the caller</li>
* <li>{@link #insert} which inserts new data into the content provider</li>
* <li>{@link #update} which updates existing data in the content provider</li>
@@ -64,8 +65,15 @@ import java.util.ArrayList;
* <li>{@link #getType} which returns the MIME type of data in the content provider</li>
* </ul></p>
*
- * <p>This class takes care of cross process calls so subclasses don't have to worry about which
- * process a request is coming from.</p>
+ * <p class="caution">Data access methods (such as {@link #insert} and
+ * {@link #update}) may be called from many threads at once, and must be thread-safe.
+ * Other methods (such as {@link #onCreate}) are only called from the application
+ * main thread, and must avoid performing lengthy operations. See the method
+ * descriptions for their expected thread behavior.</p>
+ *
+ * <p>Requests to {@link ContentResolver} are automatically forwarded to the appropriate
+ * ContentProvider instance, so subclasses don't have to worry about the details of
+ * cross-process calls.</p>
*/
public abstract class ContentProvider implements ComponentCallbacks {
/*
@@ -81,6 +89,21 @@ public abstract class ContentProvider implements ComponentCallbacks {
private Transport mTransport = new Transport();
+ /**
+ * Construct a ContentProvider instance. Content providers must be
+ * <a href="{@docRoot}guide/topics/manifest/provider-element.html">declared
+ * in the manifest</a>, accessed with {@link ContentResolver}, and created
+ * automatically by the system, so applications usually do not create
+ * ContentProvider instances directly.
+ *
+ * <p>At construction time, the object is uninitialized, and most fields and
+ * methods are unavailable. Subclasses should initialize themselves in
+ * {@link #onCreate}, not the constructor.
+ *
+ * <p>Content providers are created on the application main thread at
+ * application launch time. The constructor must not perform lengthy
+ * operations, or application startup will be delayed.
+ */
public ContentProvider() {
}
@@ -328,8 +351,8 @@ public abstract class ContentProvider implements ComponentCallbacks {
/**
- * Retrieve the Context this provider is running in. Only available once
- * onCreate(Map icicle) has been called -- this will be null in the
+ * Retrieves the Context this provider is running in. Only available once
+ * {@link #onCreate} has been called -- this will return null in the
* constructor.
*/
public final Context getContext() {
@@ -403,23 +426,59 @@ public abstract class ContentProvider implements ComponentCallbacks {
}
/**
- * Called when the provider is being started.
+ * Implement this to initialize your content provider on startup.
+ * This method is called for all registered content providers on the
+ * application main thread at application launch time. It must not perform
+ * lengthy operations, or application startup will be delayed.
+ *
+ * <p>You should defer nontrivial initialization (such as opening,
+ * upgrading, and scanning databases) until the content provider is used
+ * (via {@link #query}, {@link #insert}, etc). Deferred initialization
+ * keeps application startup fast, avoids unnecessary work if the provider
+ * turns out not to be needed, and stops database errors (such as a full
+ * disk) from halting application launch.
+ *
+ * <p>If you use SQLite, {@link android.database.sqlite.SQLiteOpenHelper}
+ * is a helpful utility class that makes it easy to manage databases,
+ * and will automatically defer opening until first use. If you do use
+ * SQLiteOpenHelper, make sure to avoid calling
+ * {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} or
+ * {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase}
+ * from this method. (Instead, override
+ * {@link android.database.sqlite.SQLiteOpenHelper#onOpen} to initialize the
+ * database when it is first opened.)
*
* @return true if the provider was successfully loaded, false otherwise
*/
public abstract boolean onCreate();
+ /**
+ * {@inheritDoc}
+ * This method is always called on the application main thread, and must
+ * not perform lengthy operations.
+ *
+ * <p>The default content provider implementation does nothing.
+ * Override this method to take appropriate action.
+ * (Content providers do not usually care about things like screen
+ * orientation, but may want to know about locale changes.)
+ */
public void onConfigurationChanged(Configuration newConfig) {
}
-
+
+ /**
+ * {@inheritDoc}
+ * This method is always called on the application main thread, and must
+ * not perform lengthy operations.
+ *
+ * <p>The default content provider implementation does nothing.
+ * Subclasses may override this method to take appropriate action.
+ */
public void onLowMemory() {
}
/**
- * Receives a query request from a client in a local process, and
- * returns a Cursor. This is called internally by the {@link ContentResolver}.
- * This method can be called from multiple
- * threads, as described in
+ * Implement this to handle query requests from clients.
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
* <p>
@@ -476,11 +535,11 @@ public abstract class ContentProvider implements ComponentCallbacks {
String selection, String[] selectionArgs, String sortOrder);
/**
- * Return the MIME type of the data at the given URI. This should start with
+ * Implement this to handle requests for the MIME type of the data at the
+ * given URI. The returned MIME type should start with
* <code>vnd.android.cursor.item</code> for a single record,
* or <code>vnd.android.cursor.dir/</code> for multiple items.
- * This method can be called from multiple
- * threads, as described in
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
*
@@ -490,11 +549,10 @@ public abstract class ContentProvider implements ComponentCallbacks {
public abstract String getType(Uri uri);
/**
- * Implement this to insert a new row.
+ * Implement this to handle requests to insert a new row.
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
* after inserting.
- * This method can be called from multiple
- * threads, as described in
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
* @param uri The content:// URI of the insertion request.
@@ -504,12 +562,12 @@ public abstract class ContentProvider implements ComponentCallbacks {
public abstract Uri insert(Uri uri, ContentValues values);
/**
- * Implement this to insert a set of new rows, or the default implementation will
- * iterate over the values and call {@link #insert} on each of them.
+ * Override this to handle requests to insert a set of new rows, or the
+ * default implementation will iterate over the values and call
+ * {@link #insert} on each of them.
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
* after inserting.
- * This method can be called from multiple
- * threads, as described in
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
*
@@ -526,13 +584,12 @@ public abstract class ContentProvider implements ComponentCallbacks {
}
/**
- * A request to delete one or more rows. The selection clause is applied when performing
- * the deletion, allowing the operation to affect multiple rows in a
- * directory.
+ * Implement this to handle requests to delete one or more rows.
+ * The implementation should apply the selection clause when performing
+ * deletion, allowing the operation to affect multiple rows in a directory.
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyDelete()}
* after deleting.
- * This method can be called from multiple
- * threads, as described in
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
*
@@ -549,13 +606,12 @@ public abstract class ContentProvider implements ComponentCallbacks {
public abstract int delete(Uri uri, String selection, String[] selectionArgs);
/**
- * Update a content URI. All rows matching the optionally provided selection
- * will have their columns listed as the keys in the values map with the
- * values of those keys.
+ * Implement this to handle requests to update one or more rows.
+ * The implementation should update all rows matching the selection
+ * to set the columns according to the provided values map.
* As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
* after updating.
- * This method can be called from multiple
- * threads, as described in
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
*
@@ -570,18 +626,15 @@ public abstract class ContentProvider implements ComponentCallbacks {
String[] selectionArgs);
/**
- * Open a file blob associated with a content URI.
- * This method can be called from multiple
- * threads, as described in
+ * Override this to handle requests to open a file blob.
+ * The default implementation always throws {@link FileNotFoundException}.
+ * This method can be called from multiple threads, as described in
* <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
* Processes and Threads</a>.
- *
- * <p>Returns a
- * ParcelFileDescriptor, from which you can obtain a
- * {@link java.io.FileDescriptor} for use with
- * {@link java.io.FileInputStream}, {@link java.io.FileOutputStream}, etc.
- * This can be used to store large data (such as an image) associated with
- * a particular piece of content.
+ *
+ * <p>This method returns a ParcelFileDescriptor, which is returned directly
+ * to the caller. This way large data (such as images and documents) can be
+ * returned without copying the content.
*
* <p>The returned ParcelFileDescriptor is owned by the caller, so it is
* their responsibility to close it when done. That is, the implementation
@@ -599,31 +652,35 @@ public abstract class ContentProvider implements ComponentCallbacks {
* no file associated with the given URI or the mode is invalid.
* @throws SecurityException Throws SecurityException if the caller does
* not have permission to access the file.
- *
+ *
* @see #openAssetFile(Uri, String)
* @see #openFileHelper(Uri, String)
- */
+ */
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
throw new FileNotFoundException("No files supported by provider at "
+ uri);
}
-
+
/**
* This is like {@link #openFile}, but can be implemented by providers
* that need to be able to return sub-sections of files, often assets
- * inside of their .apk. Note that when implementing this your clients
- * must be able to deal with such files, either directly with
- * {@link ContentResolver#openAssetFileDescriptor
- * ContentResolver.openAssetFileDescriptor}, or by using the higher-level
+ * inside of their .apk.
+ * This method can be called from multiple threads, as described in
+ * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
+ * Processes and Threads</a>.
+ *
+ * <p>If you implement this, your clients must be able to deal with such
+ * file slices, either directly with
+ * {@link ContentResolver#openAssetFileDescriptor}, or by using the higher-level
* {@link ContentResolver#openInputStream ContentResolver.openInputStream}
* or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream}
* methods.
- *
- * <p><em>Note: if you are implementing this to return a full file, you
+ *
+ * <p class="note">If you are implementing this to return a full file, you
* should create the AssetFileDescriptor with
* {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with
- * applications that can not handle sub-sections of files.</em></p>
+ * applications that can not handle sub-sections of files.</p>
*
* @param uri The URI whose file is to be opened.
* @param mode Access mode for the file. May be "r" for read-only access,
@@ -735,17 +792,21 @@ public abstract class ContentProvider implements ComponentCallbacks {
}
/**
- * Applies each of the {@link ContentProviderOperation} objects and returns an array
- * of their results. Passes through OperationApplicationException, which may be thrown
- * by the call to {@link ContentProviderOperation#apply}.
- * If all the applications succeed then a {@link ContentProviderResult} array with the
- * same number of elements as the operations will be returned. It is implementation-specific
- * how many, if any, operations will have been successfully applied if a call to
- * apply results in a {@link OperationApplicationException}.
+ * Override this to handle requests to perform a batch of operations, or the
+ * default implementation will iterate over the operations and call
+ * {@link ContentProviderOperation#apply} on each of them.
+ * If all calls to {@link ContentProviderOperation#apply} succeed
+ * then a {@link ContentProviderResult} array with as many
+ * elements as there were operations will be returned. If any of the calls
+ * fail, it is up to the implementation how many of the others take effect.
+ * This method can be called from multiple threads, as described in
+ * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
+ * Processes and Threads</a>.
+ *
* @param operations the operations to apply
* @return the results of the applications
- * @throws OperationApplicationException thrown if an application fails.
- * See {@link ContentProviderOperation#apply} for more information.
+ * @throws OperationApplicationException thrown if any operation fails.
+ * @see ContentProviderOperation#apply
*/
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 70baaef..bd23db4 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -45,7 +45,6 @@ import java.util.Arrays;
* &lt;item android:state_focused="true" android:color="@color/testcolor1"/&gt;
* &lt;item android:state_pressed="true" android:state_enabled="false" android:color="@color/testcolor2" /&gt;
* &lt;item android:state_enabled="false" android:color="@color/testcolor3" /&gt;
- * &lt;item android:state_active="true" android:color="@color/testcolor4" /&gt;
* &lt;item android:color="@color/testcolor5"/&gt;
* &lt;/selector&gt;
* </pre>
@@ -56,6 +55,9 @@ import java.util.Arrays;
* An item with no state spec is considered to match any set of states and is generally useful as
* a final item to be used as a default. Note that if you have such an item before any other items
* in the list then any subsequent items will end up being ignored.
+ * <p>For more information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/color-list-resource.html">Color State
+ * List Resource</a>.</p>
*/
public class ColorStateList implements Parcelable {
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 52aac3a..47002b6 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -22,10 +22,16 @@ import android.util.Log;
/**
* A helper class to manage database creation and version management.
- * You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
+ *
+ * <p>You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
* optionally {@link #onOpen}, and this class takes care of opening the database
* if it exists, creating it if it does not, and upgrading it as necessary.
* Transactions are used to make sure the database is always in a sensible state.
+ *
+ * <p>This class makes it easy for {@link android.content.ContentProvider}
+ * implementations to defer opening and upgrading the database until first use,
+ * to avoid blocking application startup with long-running database upgrades.
+ *
* <p>For an example, see the NotePadProvider class in the NotePad sample application,
* in the <em>samples/</em> directory of the SDK.</p>
*/
@@ -42,8 +48,9 @@ public abstract class SQLiteOpenHelper {
/**
* Create a helper object to create, open, and/or manage a database.
- * The database is not actually created or opened until one of
- * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
+ * This method always returns very quickly. The database is not actually
+ * created or opened until one of {@link #getWritableDatabase} or
+ * {@link #getReadableDatabase} is called.
*
* @param context to use to open or create the database
* @param name of the database file, or null for an in-memory database
@@ -62,13 +69,20 @@ public abstract class SQLiteOpenHelper {
/**
* Create and/or open a database that will be used for reading and writing.
- * Once opened successfully, the database is cached, so you can call this
- * method every time you need to write to the database. Make sure to call
- * {@link #close} when you no longer need it.
+ * The first time this is called, the database will be opened and
+ * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
+ * called.
*
- * <p>Errors such as bad permissions or a full disk may cause this operation
+ * <p>Once opened successfully, the database is cached, so you can
+ * call this method every time you need to write to the database.
+ * (Make sure to call {@link #close} when you no longer need the database.)
+ * Errors such as bad permissions or a full disk may cause this method
* to fail, but future attempts may succeed if the problem is fixed.</p>
*
+ * <p class="caution">Database upgrade may take a long time, you
+ * should not call this method from the application main thread, including
+ * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
+ *
* @throws SQLiteException if the database cannot be opened for writing
* @return a read/write database object valid until {@link #close} is called
*/
@@ -141,6 +155,11 @@ public abstract class SQLiteOpenHelper {
* database object will be closed and the read/write object will be returned
* in the future.
*
+ * <p class="caution">Like {@link #getWritableDatabase}, this method may
+ * take a long time to return, so you should not call it from the
+ * application main thread, including from
+ * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
+ *
* @throws SQLiteException if the database cannot be opened
* @return a database object valid until {@link #getWritableDatabase}
* or {@link #close} is called.
@@ -219,9 +238,9 @@ public abstract class SQLiteOpenHelper {
public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
/**
- * Called when the database has been opened.
- * Override method should check {@link SQLiteDatabase#isReadOnly} before
- * updating the database.
+ * Called when the database has been opened. The implementation
+ * should check {@link SQLiteDatabase#isReadOnly} before updating the
+ * database.
*
* @param db The database.
*/
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 8687a89..59c386d 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -32,23 +32,79 @@ import android.os.Looper;
import android.os.Message;
/**
- * The Camera class is used to connect/disconnect with the camera service,
- * set capture settings, start/stop preview, snap a picture, and retrieve
- * frames for encoding for video.
- * <p>There is no default constructor for this class. Use {@link #open()} to
- * get a Camera object.</p>
+ * The Camera class is used to set image capture settings, start/stop preview,
+ * snap pictures, and retrieve frames for encoding for video. This class is a
+ * client for the Camera service, which manages the actual camera hardware.
*
- * <p>In order to use the device camera, you must declare the
+ * <p>To access the device camera, you must declare the
* {@link android.Manifest.permission#CAMERA} permission in your Android
* Manifest. Also be sure to include the
* <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">&lt;uses-feature></a>
- * manifest element in order to declare camera features used by your application.
+ * manifest element to declare camera features used by your application.
* For example, if you use the camera and auto-focus feature, your Manifest
* should include the following:</p>
* <pre> &lt;uses-permission android:name="android.permission.CAMERA" />
* &lt;uses-feature android:name="android.hardware.camera" />
* &lt;uses-feature android:name="android.hardware.camera.autofocus" /></pre>
*
+ * <p>To take pictures with this class, use the following steps:</p>
+ *
+ * <ol>
+ * <li>Obtain an instance of Camera from {@link #open()}.
+ *
+ * <li>Get existing (default) settings with {@link #getParameters()}.
+ *
+ * <li>If necessary, modify the returned {@link Camera.Parameters} object and call
+ * {@link #setParameters(Camera.Parameters)}.
+ *
+ * <li>If desired, call {@link #setDisplayOrientation(int)}.
+ *
+ * <li><b>Important</b>: Pass a fully initialized {@link SurfaceHolder} to
+ * {@link #setPreviewDisplay(SurfaceHolder)}. Without a surface, the camera
+ * will be unable to start the preview.
+ *
+ * <li><b>Important</b>: Call {@link #startPreview()} to start updating the
+ * preview surface. Preview must be started before you can take a picture.
+ *
+ * <li>When you want, call {@link #takePicture(Camera.ShutterCallback,
+ * Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)} to
+ * capture a photo. Wait for the callbacks to provide the actual image data.
+ *
+ * <li>After taking a picture, preview display will have stopped. To take more
+ * photos, call {@link #startPreview()} again first.
+ *
+ * <li>Call {@link #stopPreview()} to stop updating the preview surface.
+ *
+ * <li><b>Important:</b> Call {@link #release()} to release the camera for
+ * use by other applications. Applications should release the camera
+ * immediately in {@link android.app.Activity#onPause()} (and re-{@link #open()}
+ * it in {@link android.app.Activity#onResume()}).
+ * </ol>
+ *
+ * <p>To quickly switch to video recording mode, use these steps:</p>
+ *
+ * <ol>
+ * <li>Obtain and initialize a Camera and start preview as described above.
+ *
+ * <li>Call {@link #unlock()} to allow the media process to access the camera.
+ *
+ * <li>Pass the camera to {@link android.media.MediaRecorder#setCamera(Camera)}.
+ * See {@link android.media.MediaRecorder} information about video recording.
+ *
+ * <li>When finished recording, call {@link #reconnect()} to re-acquire
+ * and re-lock the camera.
+ *
+ * <li>If desired, restart preview and take more photos or videos.
+ *
+ * <li>Call {@link #stopPreview()} and {@link #release()} as described above.
+ * </ol>
+ *
+ * <p>This class is not thread-safe, and is meant for use from one event thread.
+ * Most long-running operations (preview, focus, photo capture, etc) happen
+ * asynchronously and invoke callbacks as necessary. Callbacks will be invoked
+ * on the event thread {@link #open()} was called from. This class's methods
+ * must never be called from multiple threads at once.</p>
+ *
* <p class="caution"><strong>Caution:</strong> Different Android-powered devices
* may have different hardware specifications, such as megapixel ratings and
* auto-focus capabilities. In order for your application to be compatible with
@@ -84,7 +140,26 @@ public class Camera {
private boolean mWithBuffer;
/**
- * Returns a new Camera object.
+ * Creates a new Camera object.
+ *
+ * <p>You must call {@link #release()} when you are done using the camera,
+ * otherwise it will remain locked and be unavailable to other applications.
+ *
+ * <p>Your application should only have one Camera object active at a time.
+ *
+ * <p>Callbacks from other methods are delivered to the event loop of the
+ * thread which called open(). If this thread has no event loop, then
+ * callbacks are delivered to the main application event loop. If there
+ * is no main application event loop, callbacks are not delivered.
+ *
+ * <p class="caution"><b>Caution:</b> On some devices, this method may
+ * take a long time to complete. It is best to call this method from a
+ * worker thread (possibly using {@link android.os.AsyncTask}) to avoid
+ * blocking the main application UI thread.
+ *
+ * @return a new Camera object, connected, locked and ready for use.
+ * @throws RuntimeException if connection to the camera service fails (for
+ * example, if the camera is in use by another process).
*/
public static Camera open() {
return new Camera();
@@ -120,56 +195,83 @@ public class Camera {
/**
* Disconnects and releases the Camera object resources.
- * <p>It is recommended that you call this as soon as you're done with the
- * Camera object.</p>
+ *
+ * <p>You must call this as soon as you're done with the Camera object.</p>
*/
public final void release() {
native_release();
}
/**
- * Reconnect to the camera after passing it to MediaRecorder. To save
- * setup/teardown time, a client of Camera can pass an initialized Camera
- * object to a MediaRecorder to use for video recording. Once the
- * MediaRecorder is done with the Camera, this method can be used to
- * re-establish a connection with the camera hardware. NOTE: The Camera
- * object must first be unlocked by the process that owns it before it
- * can be connected to another process.
+ * Unlocks the camera to allow another process to access it.
+ * Normally, the camera is locked to the process with an active Camera
+ * object until {@link #release()} is called. To allow rapid handoff
+ * between processes, you can call this method to release the camera
+ * temporarily for another process to use; once the other process is done
+ * you can call {@link #reconnect()} to reclaim the camera.
+ *
+ * <p>This must be done before calling
+ * {@link android.media.MediaRecorder#setCamera(Camera)}.
+ *
+ * <p>If you are not recording video, you probably do not need this method.
*
- * @throws IOException if the method fails.
+ * @throws RuntimeException if the camera cannot be unlocked.
*/
- public native final void reconnect() throws IOException;
+ public native final void unlock();
/**
- * Lock the camera to prevent other processes from accessing it. To save
- * setup/teardown time, a client of Camera can pass an initialized Camera
- * object to another process. This method is used to re-lock the Camera
- * object prevent other processes from accessing it. By default, the
- * Camera object is locked. Locking it again from the same process will
- * have no effect. Attempting to lock it from another process if it has
- * not been unlocked will fail.
+ * Re-locks the camera to prevent other processes from accessing it.
+ * Camera objects are locked by default unless {@link #unlock()} is
+ * called. Normally {@link #reconnect()} is used instead.
*
- * @throws RuntimeException if the method fails.
+ * <p>If you are not recording video, you probably do not need this method.
+ *
+ * @throws RuntimeException if the camera cannot be re-locked (for
+ * example, if the camera is still in use by another process).
*/
public native final void lock();
/**
- * Unlock the camera to allow another process to access it. To save
- * setup/teardown time, a client of Camera can pass an initialized Camera
- * object to another process. This method is used to unlock the Camera
- * object before handing off the Camera object to the other process.
+ * Reconnects to the camera service after another process used it.
+ * After {@link #unlock()} is called, another process may use the
+ * camera; when the process is done, you must reconnect to the camera,
+ * which will re-acquire the lock and allow you to continue using the
+ * camera.
*
- * @throws RuntimeException if the method fails.
+ * <p>This must be done after {@link android.media.MediaRecorder} is
+ * done recording if {@link android.media.MediaRecorder#setCamera(Camera)}
+ * was used.
+ *
+ * <p>If you are not recording video, you probably do not need this method.
+ *
+ * @throws IOException if a connection cannot be re-established (for
+ * example, if the camera is still in use by another process).
*/
- public native final void unlock();
+ public native final void reconnect() throws IOException;
/**
- * Sets the SurfaceHolder to be used for a picture preview. If the surface
- * changed since the last call, the screen will blank. Nothing happens
- * if the same surface is re-set.
+ * Sets the {@link Surface} to be used for live preview.
+ * A surface is necessary for preview, and preview is necessary to take
+ * pictures. The same surface can be re-set without harm.
*
- * @param holder the SurfaceHolder upon which to place the picture preview
- * @throws IOException if the method fails.
+ * <p>The {@link SurfaceHolder} must already contain a surface when this
+ * method is called. If you are using {@link android.view.SurfaceView},
+ * you will need to register a {@link SurfaceHolder.Callback} with
+ * {@link SurfaceHolder#addCallback(SurfaceHolder.Callback)} and wait for
+ * {@link SurfaceHolder.Callback#surfaceCreated(SurfaceHolder)} before
+ * calling setPreviewDisplay() or starting preview.
+ *
+ * <p>This method must be called before {@link #startPreview()}. The
+ * one exception is that if the preview surface is not set (or set to null)
+ * before startPreview() is called, then this method may be called once
+ * with a non-null parameter to set the preview surface. (This allows
+ * camera setup and surface creation to happen in parallel, saving time.)
+ * The preview surface may not otherwise change while preview is running.
+ *
+ * @param holder containing the Surface on which to place the preview,
+ * or null to remove the preview surface
+ * @throws IOException if the method fails (for example, if the surface
+ * is unavailable or unsuitable).
*/
public final void setPreviewDisplay(SurfaceHolder holder) throws IOException {
if (holder != null) {
@@ -182,31 +284,47 @@ public class Camera {
private native final void setPreviewDisplay(Surface surface);
/**
- * Used to get a copy of each preview frame.
+ * Callback interface used to deliver copies of preview frames as
+ * they are displayed.
+ *
+ * @see #setPreviewCallback(Camera.PreviewCallback)
+ * @see #setOneShotPreviewCallback(Camera.PreviewCallback)
+ * @see #setPreviewCallbackWithBuffer(Camera.PreviewCallback)
+ * @see #startPreview()
*/
public interface PreviewCallback
{
/**
- * The callback that delivers the preview frames.
+ * Called as preview frames are displayed. This callback is invoked
+ * on the event thread {@link #open()} was called from.
*
- * @param data The contents of the preview frame in the format defined
+ * @param data the contents of the preview frame in the format defined
* by {@link android.graphics.ImageFormat}, which can be queried
* with {@link android.hardware.Camera.Parameters#getPreviewFormat()}.
* If {@link android.hardware.Camera.Parameters#setPreviewFormat(int)}
* is never called, the default will be the YCbCr_420_SP
* (NV21) format.
- * @param camera The Camera service object.
+ * @param camera the Camera service object.
*/
void onPreviewFrame(byte[] data, Camera camera);
};
/**
- * Start drawing preview frames to the surface.
+ * Starts capturing and drawing preview frames to the screen.
+ * Preview will not actually start until a surface is supplied with
+ * {@link #setPreviewDisplay(SurfaceHolder)}.
+ *
+ * <p>If {@link #setPreviewCallback(Camera.PreviewCallback)},
+ * {@link #setOneShotPreviewCallback(Camera.PreviewCallback)}, or
+ * {@link #setPreviewCallbackWithBuffer(Camera.PreviewCallback)} were
+ * called, {@link Camera.PreviewCallback#onPreviewFrame(byte[], Camera)}
+ * will be called when preview data becomes available.
*/
public native final void startPreview();
/**
- * Stop drawing preview frames to the surface.
+ * Stops capturing and drawing preview frames to the surface, and
+ * resets the camera for a future call to {@link #startPreview()}.
*/
public native final void stopPreview();
@@ -219,11 +337,13 @@ public class Camera {
public native final boolean previewEnabled();
/**
- * Can be called at any time to instruct the camera to use a callback for
- * each preview frame in addition to displaying it.
+ * Installs a callback to be invoked for every preview frame in addition
+ * to displaying them on the screen. The callback will be repeatedly called
+ * for as long as preview is active. This method can be called at any time,
+ * even while preview is live. Any other preview callbacks are overridden.
*
- * @param cb A callback object that receives a copy of each preview frame.
- * Pass null to stop receiving callbacks at any time.
+ * @param cb a callback object that receives a copy of each preview frame,
+ * or null to stop receiving callbacks.
*/
public final void setPreviewCallback(PreviewCallback cb) {
mPreviewCallback = cb;
@@ -235,10 +355,13 @@ public class Camera {
}
/**
- * Installs a callback to retrieve a single preview frame, after which the
- * callback is cleared.
+ * Installs a callback to be invoked for the next preview frame in addition
+ * to displaying it on the screen. After one invocation, the callback is
+ * cleared. This method can be called any time, even when preview is live.
+ * Any other preview callbacks are overridden.
*
- * @param cb A callback object that receives a copy of the preview frame.
+ * @param cb a callback object that receives a copy of the next preview frame,
+ * or null to stop receiving callbacks.
*/
public final void setOneShotPreviewCallback(PreviewCallback cb) {
mPreviewCallback = cb;
@@ -250,17 +373,24 @@ public class Camera {
private native final void setHasPreviewCallback(boolean installed, boolean manualBuffer);
/**
- * Installs a callback which will get called as long as there are buffers in the
- * preview buffer queue, which minimizes dynamic allocation of preview buffers.
+ * Installs a callback to be invoked for every preview frame, using buffers
+ * supplied with {@link #addCallbackBuffer(byte[])}, in addition to
+ * displaying them on the screen. The callback will be repeatedly called
+ * for as long as preview is active and buffers are available.
+ * Any other preview callbacks are overridden.
*
- * Apps must call addCallbackBuffer to explicitly register the buffers to use, or no callbacks
- * will be received. addCallbackBuffer may be safely called before or after
- * a call to setPreviewCallbackWithBuffer with a non-null callback parameter.
+ * <p>The purpose of this method is to improve preview efficiency and frame
+ * rate by allowing preview frame memory reuse. You must call
+ * {@link #addCallbackBuffer(byte[])} at some point -- before or after
+ * calling this method -- or no callbacks will received.
*
- * The buffer queue will be cleared upon any calls to setOneShotPreviewCallback,
- * setPreviewCallback, or to this method with a null callback parameter.
+ * The buffer queue will be cleared if this method is called with a null
+ * callback, {@link #setPreviewCallback(Camera.PreviewCallback)} is called,
+ * or {@link #setOneShotPreviewCallback(Camera.PreviewCallback)} is called.
*
- * @param cb A callback object that receives a copy of the preview frame. A null value will clear the queue.
+ * @param cb a callback object that receives a copy of the preview frame,
+ * or null to stop receiving callbacks and clear the buffer queue.
+ * @see #addCallbackBuffer(byte[])
*/
public final void setPreviewCallbackWithBuffer(PreviewCallback cb) {
mPreviewCallback = cb;
@@ -272,21 +402,27 @@ public class Camera {
/**
* Adds a pre-allocated buffer to the preview callback buffer queue.
* Applications can add one or more buffers to the queue. When a preview
- * frame arrives and there is still available buffer, buffer will be filled
- * and it is removed from the queue. Then preview callback is invoked with
- * the buffer. If a frame arrives and there is no buffer left, the frame is
- * discarded. Applications should add the buffers back when they finish the
- * processing.
+ * frame arrives and there is still at least one available buffer, the
+ * buffer will be used and removed from the queue. Then preview callback is
+ * invoked with the buffer. If a frame arrives and there is no buffer left,
+ * the frame is discarded. Applications should add buffers back when they
+ * finish processing the data in them.
*
- * The image format of the callback buffer can be read from {@link
- * android.hardware.Camera.Parameters#getPreviewFormat()}. bitsPerPixel can
- * be read from {@link android.graphics.ImageFormat#getBitsPerPixel(int)}.
- * Preview width and height can be determined from getPreviewSize.
+ * <p>The size of the buffer is determined by multiplying the preview
+ * image width, height, and bytes per pixel. The width and height can be
+ * read from {@link Camera.Parameters#getPreviewSize()}. Bytes per pixel
+ * can be computed from
+ * {@link android.graphics.ImageFormat#getBitsPerPixel(int)} / 8,
+ * using the image format from {@link Camera.Parameters#getPreviewFormat()}.
*
- * Alternatively, a buffer from a previous callback may be passed in or used
- * to determine the size of new preview frame buffers.
+ * <p>This method is only necessary when
+ * {@link #setPreviewCallbackWithBuffer(PreviewCallback)} is used. When
+ * {@link #setPreviewCallback(PreviewCallback)} or
+ * {@link #setOneShotPreviewCallback(PreviewCallback)} are used, buffers
+ * are automatically allocated.
*
- * @param callbackBuffer The buffer to register. Size should be width * height * bitsPerPixel / 8.
+ * @param callbackBuffer the buffer to add to the queue.
+ * The size should be width * height * bits_per_pixel / 8.
* @see #setPreviewCallbackWithBuffer(PreviewCallback)
*/
public native final void addCallbackBuffer(byte[] callbackBuffer);
@@ -385,7 +521,8 @@ public class Camera {
}
/**
- * Handles the callback for the camera auto focus.
+ * Callback interface used to notify on completion of camera auto focus.
+ *
* <p>Devices that do not support auto-focus will receive a "fake"
* callback to this interface. If your application needs auto-focus and
* should not be installed on devices <em>without</em> auto-focus, you must
@@ -393,13 +530,15 @@ public class Camera {
* {@code android.hardware.camera.autofocus} feature, in the
* <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">&lt;uses-feature></a>
* manifest element.</p>
+ *
+ * @see #autoFocus(AutoFocusCallback)
*/
public interface AutoFocusCallback
{
/**
- * Callback for the camera auto focus. If the camera does not support
- * auto-focus and autoFocus is called, onAutoFocus will be called
- * immediately with success.
+ * Called when the camera auto focus completes. If the camera does not
+ * support auto-focus and autoFocus is called, onAutoFocus will be
+ * called immediately with success.
*
* @param success true if focus was successful, false if otherwise
* @param camera the Camera service object
@@ -408,23 +547,28 @@ public class Camera {
};
/**
- * Starts auto-focus function and registers a callback function to run when
- * camera is focused. Only valid after startPreview() has been called.
- * Applications should call {@link
- * android.hardware.Camera.Parameters#getFocusMode()} to determine if this
- * method should be called. If the camera does not support auto-focus, it is
- * a no-op and {@link AutoFocusCallback#onAutoFocus(boolean, Camera)}
+ * Starts camera auto-focus and registers a callback function to run when
+ * the camera is focused. This method is only valid when preview is active
+ * (between {@link #startPreview()} and before {@link #stopPreview()}).
+ *
+ * <p>Callers should check
+ * {@link android.hardware.Camera.Parameters#getFocusMode()} to determine if
+ * this method should be called. If the camera does not support auto-focus,
+ * it is a no-op and {@link AutoFocusCallback#onAutoFocus(boolean, Camera)}
* callback will be called immediately.
+ *
* <p>If your application should not be installed
* on devices without auto-focus, you must declare that your application
* uses auto-focus with the
* <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">&lt;uses-feature></a>
* manifest element.</p>
+ *
* <p>If the current flash mode is not
* {@link android.hardware.Camera.Parameters#FLASH_MODE_OFF}, flash may be
- * fired during auto-focus depending on the driver.<p>
+ * fired during auto-focus, depending on the driver and camera hardware.<p>
*
* @param cb the callback to run
+ * @see #cancelAutoFocus()
*/
public final void autoFocus(AutoFocusCallback cb)
{
@@ -434,10 +578,12 @@ public class Camera {
private native final void native_autoFocus();
/**
- * Cancels auto-focus function. If the auto-focus is still in progress,
- * this function will cancel it. Whether the auto-focus is in progress
- * or not, this function will return the focus position to the default.
+ * Cancels any auto-focus function in progress.
+ * Whether or not auto-focus is currently in progress,
+ * this function will return the focus position to the default.
* If the camera does not support auto-focus, this is a no-op.
+ *
+ * @see #autoFocus(Camera.AutoFocusCallback)
*/
public final void cancelAutoFocus()
{
@@ -447,23 +593,32 @@ public class Camera {
private native final void native_cancelAutoFocus();
/**
- * An interface which contains a callback for the shutter closing after taking a picture.
+ * Callback interface used to signal the moment of actual image capture.
+ *
+ * @see #takePicture(ShutterCallback, PictureCallback, PictureCallback, PictureCallback)
*/
public interface ShutterCallback
{
/**
- * Can be used to play a shutter sound as soon as the image has been captured, but before
- * the data is available.
+ * Called as near as possible to the moment when a photo is captured
+ * from the sensor. This is a good opportunity to play a shutter sound
+ * or give other feedback of camera operation. This may be some time
+ * after the photo was triggered, but some time before the actual data
+ * is available.
*/
void onShutter();
}
/**
- * Handles the callback for when a picture is taken.
+ * Callback interface used to supply image data from a photo capture.
+ *
+ * @see #takePicture(ShutterCallback, PictureCallback, PictureCallback, PictureCallback)
*/
public interface PictureCallback {
/**
- * Callback for when a picture is taken.
+ * Called when image data is available after a picture is taken.
+ * The format of the data depends on the context of the callback
+ * and {@link Camera.Parameters} settings.
*
* @param data a byte array of the picture data
* @param camera the Camera service object
@@ -472,23 +627,9 @@ public class Camera {
};
/**
- * Triggers an asynchronous image capture. The camera service will initiate
- * a series of callbacks to the application as the image capture progresses.
- * The shutter callback occurs after the image is captured. This can be used
- * to trigger a sound to let the user know that image has been captured. The
- * raw callback occurs when the raw image data is available (NOTE: the data
- * may be null if the hardware does not have enough memory to make a copy).
- * The jpeg callback occurs when the compressed image is available. If the
- * application does not need a particular callback, a null can be passed
- * instead of a callback method.
- *
- * This method will stop the preview. Applications should not call {@link
- * #stopPreview()} before this. After jpeg callback is received,
- * applications can call {@link #startPreview()} to restart the preview.
+ * Equivalent to takePicture(shutter, raw, null, jpeg).
*
- * @param shutter callback after the image is captured, may be null
- * @param raw callback with raw image data, may be null
- * @param jpeg callback with jpeg image data, may be null
+ * @see #takePicture(ShutterCallback, PictureCallback, PictureCallback, PictureCallback)
*/
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
PictureCallback jpeg) {
@@ -509,14 +650,18 @@ public class Camera {
* application does not need a particular callback, a null can be passed
* instead of a callback method.
*
- * This method will stop the preview. Applications should not call {@link
- * #stopPreview()} before this. After jpeg callback is received,
- * applications can call {@link #startPreview()} to restart the preview.
+ * <p>This method is only valid when preview is active (after
+ * {@link #startPreview()}). Preview will be stopped after the image is
+ * taken; callers must call {@link #startPreview()} again if they want to
+ * re-start preview or take more pictures.
*
- * @param shutter callback after the image is captured, may be null
- * @param raw callback with raw image data, may be null
+ * <p>After calling this method, you must not call {@link #startPreview()}
+ * or take another picture until the JPEG callback has returned.
+ *
+ * @param shutter the callback for image capture moment, or null
+ * @param raw the callback for raw (uncompressed) image data, or null
* @param postview callback with postview image data, may be null
- * @param jpeg callback with jpeg image data, may be null
+ * @param jpeg the callback for JPEG image data, or null
*/
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
PictureCallback postview, PictureCallback jpeg) {
@@ -528,26 +673,29 @@ public class Camera {
}
/**
- * Zooms to the requested value smoothly. Driver will notify {@link
+ * Zooms to the requested value smoothly. The driver will notify {@link
* OnZoomChangeListener} of the zoom value and whether zoom is stopped at
* the time. For example, suppose the current zoom is 0 and startSmoothZoom
- * is called with value 3. Method onZoomChange will be called three times
- * with zoom value 1, 2, and 3. The applications can call {@link
- * #stopSmoothZoom} to stop the zoom earlier. The applications should not
- * call startSmoothZoom again or change the zoom value before zoom stops. If
- * the passing zoom value equals to the current zoom value, no zoom callback
- * will be generated. This method is supported if {@link
- * android.hardware.Camera.Parameters#isSmoothZoomSupported} is true.
+ * is called with value 3. The
+ * {@link Camera.OnZoomChangeListener#onZoomChange(int, boolean, Camera)}
+ * method will be called three times with zoom values 1, 2, and 3.
+ * Applications can call {@link #stopSmoothZoom} to stop the zoom earlier.
+ * Applications should not call startSmoothZoom again or change the zoom
+ * value before zoom stops. If the supplied zoom value equals to the current
+ * zoom value, no zoom callback will be generated. This method is supported
+ * if {@link android.hardware.Camera.Parameters#isSmoothZoomSupported}
+ * returns true.
*
* @param value zoom value. The valid range is 0 to {@link
* android.hardware.Camera.Parameters#getMaxZoom}.
* @throws IllegalArgumentException if the zoom value is invalid.
* @throws RuntimeException if the method fails.
+ * @see #setZoomChangeListener(OnZoomChangeListener)
*/
public native final void startSmoothZoom(int value);
/**
- * Stops the smooth zoom. The applications should wait for the {@link
+ * Stops the smooth zoom. Applications should wait for the {@link
* OnZoomChangeListener} to know when the zoom is actually stopped. This
* method is supported if {@link
* android.hardware.Camera.Parameters#isSmoothZoomSupported} is true.
@@ -572,20 +720,21 @@ public class Camera {
public native final void setDisplayOrientation(int degrees);
/**
- * Interface for a callback to be invoked when zoom value changes.
+ * Callback interface for zoom changes during a smooth zoom operation.
+ *
+ * @see #setZoomChangeListener(OnZoomChangeListener)
+ * @see #startSmoothZoom(int)
*/
public interface OnZoomChangeListener
{
/**
- * Called when the zoom value has changed.
+ * Called when the zoom value has changed during a smooth zoom.
*
* @param zoomValue the current zoom value. In smooth zoom mode, camera
* calls this for every new zoom value.
* @param stopped whether smooth zoom is stopped. If the value is true,
* this is the last zoom update for the application.
- *
* @param camera the Camera service object
- * @see #startSmoothZoom(int)
*/
void onZoomChange(int zoomValue, boolean stopped, Camera camera);
};
@@ -602,15 +751,25 @@ public class Camera {
mZoomListener = listener;
}
- // These match the enum in include/ui/Camera.h
- /** Unspecified camerar error. @see #ErrorCallback */
+ // Error codes match the enum in include/ui/Camera.h
+
+ /**
+ * Unspecified camera error.
+ * @see Camera.ErrorCallback
+ */
public static final int CAMERA_ERROR_UNKNOWN = 1;
- /** Media server died. In this case, the application must release the
- * Camera object and instantiate a new one. @see #ErrorCallback */
+
+ /**
+ * Media server died. In this case, the application must release the
+ * Camera object and instantiate a new one.
+ * @see Camera.ErrorCallback
+ */
public static final int CAMERA_ERROR_SERVER_DIED = 100;
/**
- * Handles the camera error callback.
+ * Callback interface for camera error notification.
+ *
+ * @see #setErrorCallback(ErrorCallback)
*/
public interface ErrorCallback
{
@@ -628,7 +787,7 @@ public class Camera {
/**
* Registers a callback to be invoked when an error occurs.
- * @param cb the callback to run
+ * @param cb The callback to run
*/
public final void setErrorCallback(ErrorCallback cb)
{
@@ -639,16 +798,21 @@ public class Camera {
private native final String native_getParameters();
/**
- * Sets the Parameters for pictures from this Camera service.
+ * Changes the settings for this Camera service.
*
* @param params the Parameters to use for this Camera service
+ * @see #getParameters()
*/
public void setParameters(Parameters params) {
native_setParameters(params.flatten());
}
/**
- * Returns the picture Parameters for this Camera service.
+ * Returns the current settings for this Camera service.
+ * If modifications are made to the returned Parameters, they must be passed
+ * to {@link #setParameters(Camera.Parameters)} to take effect.
+ *
+ * @see #setParameters(Camera.Parameters)
*/
public Parameters getParameters() {
Parameters p = new Parameters();
@@ -658,7 +822,7 @@ public class Camera {
}
/**
- * Handles the picture size (dimensions).
+ * Image size (width and height dimensions).
*/
public class Size {
/**
@@ -697,18 +861,21 @@ public class Camera {
};
/**
- * Handles the parameters for pictures created by a Camera service.
+ * Camera service settings.
*
* <p>To make camera parameters take effect, applications have to call
- * Camera.setParameters. For example, after setWhiteBalance is called, white
- * balance is not changed until Camera.setParameters() is called.
+ * {@link Camera#setParameters(Camera.Parameters)}. For example, after
+ * {@link Camera.Parameters#setWhiteBalance} is called, white balance is not
+ * actually changed until {@link Camera#setParameters(Camera.Parameters)}
+ * is called with the changed parameters object.
*
* <p>Different devices may have different camera capabilities, such as
* picture size or flash modes. The application should query the camera
* capabilities before setting parameters. For example, the application
- * should call getSupportedColorEffects before calling setEffect. If the
- * camera does not support color effects, getSupportedColorEffects will
- * return null.
+ * should call {@link Camera.Parameters#getSupportedColorEffects()} before
+ * calling {@link Camera.Parameters#setColorEffect(String)}. If the
+ * camera does not support color effects,
+ * {@link Camera.Parameters#getSupportedColorEffects()} will return null.
*/
public class Parameters {
// Parameter keys to communicate with the camera driver.
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index a8c6f9b..31acb5b 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -35,6 +35,11 @@ import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import javax.net.SocketFactory;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
@@ -48,17 +53,33 @@ import org.apache.harmony.xnet.provider.jsse.SSLParameters;
/**
* SSLSocketFactory implementation with several extra features:
+ *
* <ul>
* <li>Timeout specification for SSL handshake operations
+ * <li>Hostname verification in most cases (see WARNINGs below)
* <li>Optional SSL session caching with {@link SSLSessionCache}
* <li>Optionally bypass all SSL certificate checks
* </ul>
- * Note that the handshake timeout does not apply to actual connection.
- * If you want a connection timeout as well, use {@link #createSocket()} and
- * {@link Socket#connect(SocketAddress, int)}.
- * <p>
- * On development devices, "setprop socket.relaxsslcheck yes" bypasses all
- * SSL certificate checks, for testing with development servers.
+ *
+ * The handshake timeout does not apply to actual TCP socket connection.
+ * If you want a connection timeout as well, use {@link #createSocket()}
+ * and {@link Socket#connect(SocketAddress, int)}, after which you
+ * must verify the identity of the server you are connected to.
+ *
+ * <p class="caution"><b>Most {@link SSLSocketFactory} implementations do not
+ * verify the server's identity, allowing man-in-the-middle attacks.</b>
+ * This implementation does check the server's certificate hostname, but only
+ * for createSocket variants that specify a hostname. When using methods that
+ * use {@link InetAddress} or which return an unconnected socket, you MUST
+ * verify the server's identity yourself to ensure a secure connection.</p>
+ *
+ * <p>One way to verify the server's identity is to use
+ * {@link HttpsURLConnection#getDefaultHostnameVerifier()} to get a
+ * {@link HostnameVerifier} to verify the certificate hostname.
+ *
+ * <p>On development devices, "setprop socket.relaxsslcheck yes" bypasses all
+ * SSL certificate and hostname checks for testing purposes. This setting
+ * requires root access.
*/
public class SSLCertificateSocketFactory extends SSLSocketFactory {
private static final String TAG = "SSLCertificateSocketFactory";
@@ -71,6 +92,9 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
}
};
+ private static final HostnameVerifier HOSTNAME_VERIFIER =
+ HttpsURLConnection.getDefaultHostnameVerifier();
+
private SSLSocketFactory mInsecureFactory = null;
private SSLSocketFactory mSecureFactory = null;
@@ -95,7 +119,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
*
* @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
* for none. The socket timeout is reset to 0 after the handshake.
- * @return a new SocketFactory with the specified parameters
+ * @return a new SSLSocketFactory with the specified parameters
*/
public static SocketFactory getDefault(int handshakeTimeoutMillis) {
return new SSLCertificateSocketFactory(handshakeTimeoutMillis, null, true);
@@ -108,7 +132,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
* @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
* for none. The socket timeout is reset to 0 after the handshake.
* @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
- * @return a new SocketFactory with the specified parameters
+ * @return a new SSLSocketFactory with the specified parameters
*/
public static SSLSocketFactory getDefault(int handshakeTimeoutMillis, SSLSessionCache cache) {
return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true);
@@ -117,13 +141,14 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
/**
* Returns a new instance of a socket factory with all SSL security checks
* disabled, using an optional handshake timeout and SSL session cache.
- * Sockets created using this factory are vulnerable to man-in-the-middle
- * attacks!
+ *
+ * <p class="caution"><b>Warning:</b> Sockets created using this factory
+ * are vulnerable to man-in-the-middle attacks!</p>
*
* @param handshakeTimeoutMillis to use for SSL connection handshake, or 0
* for none. The socket timeout is reset to 0 after the handshake.
* @param cache The {@link SSLClientSessionCache} to use, or null for no cache.
- * @return an insecure SocketFactory with the specified parameters
+ * @return an insecure SSLSocketFactory with the specified parameters
*/
public static SSLSocketFactory getInsecure(int handshakeTimeoutMillis, SSLSessionCache cache) {
return new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, false);
@@ -145,6 +170,44 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
new SSLCertificateSocketFactory(handshakeTimeoutMillis, cache, true));
}
+ /**
+ * Verify the hostname of the certificate used by the other end of a
+ * connected socket. You MUST call this if you did not supply a hostname
+ * to {@link #createSocket()}. It is harmless to call this method
+ * redundantly if the hostname has already been verified.
+ *
+ * <p>Wildcard certificates are allowed to verify any matching hostname,
+ * so "foo.bar.example.com" is verified if the peer has a certificate
+ * for "*.example.com".
+ *
+ * @param socket An SSL socket which has been connected to a server
+ * @param hostname The expected hostname of the remote server
+ * @throws IOException if something goes wrong handshaking with the server
+ * @throws SSLPeerUnverifiedException if the server cannot prove its identity
+ *
+ * @hide
+ */
+ public static void verifyHostname(Socket socket, String hostname) throws IOException {
+ if (!(socket instanceof SSLSocket)) {
+ throw new IllegalArgumentException("Attempt to verify non-SSL socket");
+ }
+
+ if (!isSslCheckRelaxed()) {
+ // The code at the start of OpenSSLSocketImpl.startHandshake()
+ // ensures that the call is idempotent, so we can safely call it.
+ SSLSocket ssl = (SSLSocket) socket;
+ ssl.startHandshake();
+
+ SSLSession session = ssl.getSession();
+ if (session == null) {
+ throw new SSLException("Cannot verify SSL socket without session");
+ }
+ if (!HOSTNAME_VERIFIER.verify(hostname, session)) {
+ throw new SSLPeerUnverifiedException("Cannot verify hostname: " + hostname);
+ }
+ }
+ }
+
private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) {
try {
SSLContextImpl sslContext = new SSLContextImpl();
@@ -156,10 +219,14 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
}
}
+ private static boolean isSslCheckRelaxed() {
+ return "1".equals(SystemProperties.get("ro.debuggable")) &&
+ "yes".equals(SystemProperties.get("socket.relaxsslcheck"));
+ }
+
private synchronized SSLSocketFactory getDelegate() {
// Relax the SSL check if instructed (for this factory, or systemwide)
- if (!mSecure || ("1".equals(SystemProperties.get("ro.debuggable")) &&
- "yes".equals(SystemProperties.get("socket.relaxsslcheck")))) {
+ if (!mSecure || isSslCheckRelaxed()) {
if (mInsecureFactory == null) {
if (mSecure) {
Log.w(TAG, "*** BYPASSING SSL SECURITY CHECKS (socket.relaxsslcheck=yes) ***");
@@ -177,13 +244,30 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
}
}
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This method verifies the peer's certificate hostname after connecting
+ * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
+ */
@Override
public Socket createSocket(Socket k, String host, int port, boolean close) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(k, host, port, close);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+ if (mSecure) {
+ verifyHostname(s, host);
+ }
return s;
}
+ /**
+ * Creates a new socket which is not connected to any remote host.
+ * You must use {@link Socket#connect} to connect the socket.
+ *
+ * <p class="caution"><b>Warning:</b> Hostname verification is not performed
+ * with this method. You MUST verify the server's identity after connecting
+ * the socket to avoid man-in-the-middle attacks.</p>
+ */
@Override
public Socket createSocket() throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket();
@@ -191,6 +275,13 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
return s;
}
+ /**
+ * {@inheritDoc}
+ *
+ * <p class="caution"><b>Warning:</b> Hostname verification is not performed
+ * with this method. You MUST verify the server's identity after connecting
+ * the socket to avoid man-in-the-middle attacks.</p>
+ */
@Override
public Socket createSocket(InetAddress addr, int port, InetAddress localAddr, int localPort)
throws IOException {
@@ -200,6 +291,13 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
return s;
}
+ /**
+ * {@inheritDoc}
+ *
+ * <p class="caution"><b>Warning:</b> Hostname verification is not performed
+ * with this method. You MUST verify the server's identity after connecting
+ * the socket to avoid man-in-the-middle attacks.</p>
+ */
@Override
public Socket createSocket(InetAddress addr, int port) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(addr, port);
@@ -207,19 +305,37 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory {
return s;
}
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This method verifies the peer's certificate hostname after connecting
+ * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
+ */
@Override
public Socket createSocket(String host, int port, InetAddress localAddr, int localPort)
throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(
host, port, localAddr, localPort);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+ if (mSecure) {
+ verifyHostname(s, host);
+ }
return s;
}
+ /**
+ * {@inheritDoc}
+ *
+ * <p>This method verifies the peer's certificate hostname after connecting
+ * (unless created with {@link #getInsecure(int, SSLSessionCache)}).
+ */
@Override
public Socket createSocket(String host, int port) throws IOException {
OpenSSLSocketImpl s = (OpenSSLSocketImpl) getDelegate().createSocket(host, port);
s.setHandshakeTimeout(mHandshakeTimeoutMillis);
+ if (mSecure) {
+ verifyHostname(s, host);
+ }
return s;
}
diff --git a/core/java/android/os/DropBoxManager.java b/core/java/android/os/DropBoxManager.java
index 7889a92..a9f9bd7 100644
--- a/core/java/android/os/DropBoxManager.java
+++ b/core/java/android/os/DropBoxManager.java
@@ -53,6 +53,9 @@ public class DropBoxManager {
/** Flag value: Content can be decompressed with {@link java.util.zip.GZIPOutputStream}. */
public static final int IS_GZIPPED = 4;
+ /** Flag value for serialization only: Value is a byte array, not a file descriptor */
+ private static final int HAS_BYTE_ARRAY = 8;
+
/**
* A single entry retrieved from the drop box.
* This may include a reference to a stream, so you must call
@@ -68,12 +71,25 @@ public class DropBoxManager {
/** Create a new empty Entry with no contents. */
public Entry(String tag, long millis) {
- this(tag, millis, (Object) null, IS_EMPTY);
+ if (tag == null) throw new NullPointerException("tag == null");
+
+ mTag = tag;
+ mTimeMillis = millis;
+ mData = null;
+ mFileDescriptor = null;
+ mFlags = IS_EMPTY;
}
/** Create a new Entry with plain text contents. */
public Entry(String tag, long millis, String text) {
- this(tag, millis, (Object) text.getBytes(), IS_TEXT);
+ if (tag == null) throw new NullPointerException("tag == null");
+ if (text == null) throw new NullPointerException("text == null");
+
+ mTag = tag;
+ mTimeMillis = millis;
+ mData = text.getBytes();
+ mFileDescriptor = null;
+ mFlags = IS_TEXT;
}
/**
@@ -81,7 +97,16 @@ public class DropBoxManager {
* The data array must not be modified after creating this entry.
*/
public Entry(String tag, long millis, byte[] data, int flags) {
- this(tag, millis, (Object) data, flags);
+ if (tag == null) throw new NullPointerException("tag == null");
+ if (((flags & IS_EMPTY) != 0) != (data == null)) {
+ throw new IllegalArgumentException("Bad flags: " + flags);
+ }
+
+ mTag = tag;
+ mTimeMillis = millis;
+ mData = data;
+ mFileDescriptor = null;
+ mFlags = flags;
}
/**
@@ -89,7 +114,16 @@ public class DropBoxManager {
* Takes ownership of the ParcelFileDescriptor.
*/
public Entry(String tag, long millis, ParcelFileDescriptor data, int flags) {
- this(tag, millis, (Object) data, flags);
+ if (tag == null) throw new NullPointerException("tag == null");
+ if (((flags & IS_EMPTY) != 0) != (data == null)) {
+ throw new IllegalArgumentException("Bad flags: " + flags);
+ }
+
+ mTag = tag;
+ mTimeMillis = millis;
+ mData = null;
+ mFileDescriptor = data;
+ mFlags = flags;
}
/**
@@ -97,31 +131,14 @@ public class DropBoxManager {
* The file will be read when the entry's contents are requested.
*/
public Entry(String tag, long millis, File data, int flags) throws IOException {
- this(tag, millis, (Object) ParcelFileDescriptor.open(
- data, ParcelFileDescriptor.MODE_READ_ONLY), flags);
- }
-
- /** Internal constructor for CREATOR.createFromParcel(). */
- private Entry(String tag, long millis, Object value, int flags) {
- if (tag == null) throw new NullPointerException();
- if (((flags & IS_EMPTY) != 0) != (value == null)) throw new IllegalArgumentException();
+ if (tag == null) throw new NullPointerException("tag == null");
+ if ((flags & IS_EMPTY) != 0) throw new IllegalArgumentException("Bad flags: " + flags);
mTag = tag;
mTimeMillis = millis;
+ mData = null;
+ mFileDescriptor = ParcelFileDescriptor.open(data, ParcelFileDescriptor.MODE_READ_ONLY);
mFlags = flags;
-
- if (value == null) {
- mData = null;
- mFileDescriptor = null;
- } else if (value instanceof byte[]) {
- mData = (byte[]) value;
- mFileDescriptor = null;
- } else if (value instanceof ParcelFileDescriptor) {
- mData = null;
- mFileDescriptor = (ParcelFileDescriptor) value;
- } else {
- throw new IllegalArgumentException();
- }
}
/** Close the input stream associated with this entry. */
@@ -149,6 +166,7 @@ public class DropBoxManager {
InputStream is = null;
try {
is = getInputStream();
+ if (is == null) return null;
byte[] buf = new byte[maxBytes];
return new String(buf, 0, Math.max(0, is.read(buf)));
} catch (IOException e) {
@@ -174,8 +192,14 @@ public class DropBoxManager {
public static final Parcelable.Creator<Entry> CREATOR = new Parcelable.Creator() {
public Entry[] newArray(int size) { return new Entry[size]; }
public Entry createFromParcel(Parcel in) {
- return new Entry(
- in.readString(), in.readLong(), in.readValue(null), in.readInt());
+ String tag = in.readString();
+ long millis = in.readLong();
+ int flags = in.readInt();
+ if ((flags & HAS_BYTE_ARRAY) != 0) {
+ return new Entry(tag, millis, in.createByteArray(), flags & ~HAS_BYTE_ARRAY);
+ } else {
+ return new Entry(tag, millis, in.readFileDescriptor(), flags);
+ }
}
};
@@ -187,11 +211,12 @@ public class DropBoxManager {
out.writeString(mTag);
out.writeLong(mTimeMillis);
if (mFileDescriptor != null) {
- out.writeValue(mFileDescriptor);
+ out.writeInt(mFlags & ~HAS_BYTE_ARRAY); // Clear bit just to be safe
+ mFileDescriptor.writeToParcel(out, flags);
} else {
- out.writeValue(mData);
+ out.writeInt(mFlags | HAS_BYTE_ARRAY);
+ out.writeByteArray(mData);
}
- out.writeInt(mFlags);
}
}
@@ -225,7 +250,7 @@ public class DropBoxManager {
* @param flags describing the data
*/
public void addData(String tag, byte[] data, int flags) {
- if (data == null) throw new NullPointerException();
+ if (data == null) throw new NullPointerException("data == null");
try { mService.add(new Entry(tag, 0, data, flags)); } catch (RemoteException e) {}
}
@@ -239,7 +264,7 @@ public class DropBoxManager {
* @throws IOException if the file can't be opened
*/
public void addFile(String tag, File file, int flags) throws IOException {
- if (file == null) throw new NullPointerException();
+ if (file == null) throw new NullPointerException("file == null");
Entry entry = new Entry(tag, 0, file, flags);
try {
mService.add(new Entry(tag, 0, file, flags));
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index dedc347..01cc408 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -26,6 +26,7 @@ interface IPowerManager
void releaseWakeLock(IBinder lock, int flags);
void userActivity(long when, boolean noChangeLights);
void userActivityWithForce(long when, boolean noChangeLights, boolean force);
+ void clearUserActivityTimeout(long now, long timeout);
void setPokeLock(int pokey, IBinder lock, String tag);
int getSupportedWakeLockFlags();
void setStayOnSetting(int val);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 8ad600c..bb6357e 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -437,6 +437,12 @@ public final class Parcel {
/**
* Write a FileDescriptor into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
+ *
+ * <p class="caution">The file descriptor will not be closed, which may
+ * result in file descriptor leaks when objects are returned from Binder
+ * calls. Use {@link ParcelFileDescriptor#writeToParcel} instead, which
+ * accepts contextual flags and will close the original file descriptor
+ * if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p>
*/
public final native void writeFileDescriptor(FileDescriptor val);
@@ -1000,7 +1006,7 @@ public final class Parcel {
/**
* Flatten a generic object in to a parcel. The given Object value may
* currently be one of the following types:
- *
+ *
* <ul>
* <li> null
* <li> String
@@ -1023,7 +1029,7 @@ public final class Parcel {
* <li> Parcelable[]
* <li> CharSequence (as supported by {@link TextUtils#writeToParcel}).
* <li> List (as supported by {@link #writeList}).
- * <li> {@link SparseArray} (as supported by {@link #writeSparseArray}).
+ * <li> {@link SparseArray} (as supported by {@link #writeSparseArray(SparseArray)}).
* <li> {@link IBinder}
* <li> Any object that implements Serializable (but see
* {@link #writeSerializable} for caveats). Note that all of the
@@ -1032,6 +1038,13 @@ public final class Parcel {
* approach is much less efficient and should be avoided whenever
* possible.
* </ul>
+ *
+ * <p class="caution">{@link Parcelable} objects are written with
+ * {@link Parcelable#writeToParcel} using contextual flags of 0. When
+ * serializing objects containing {@link ParcelFileDescriptor}s,
+ * this may result in file descriptor leaks when they are returned from
+ * Binder calls (where {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}
+ * should be used).</p>
*/
public final void writeValue(Object v) {
if (v == null) {
@@ -1120,7 +1133,7 @@ public final class Parcel {
/**
* Flatten the name of the class of the Parcelable and its contents
* into the parcel.
- *
+ *
* @param p The Parcelable object to be written.
* @param parcelableFlags Contextual flags as per
* {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 0a3b2cf..e8fe302 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -250,6 +250,11 @@ public class ParcelFileDescriptor implements Parcelable {
return Parcelable.CONTENTS_FILE_DESCRIPTOR;
}
+ /**
+ * {@inheritDoc}
+ * If {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set in flags,
+ * the file descriptor will be closed after a copy is written to the Parcel.
+ */
public void writeToParcel(Parcel out, int flags) {
out.writeFileDescriptor(mFileDescriptor);
if ((flags&PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 11e5ad1..f9abe60 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7890,7 +7890,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* without resorting to another data structure.
*
* The specified key should be an id declared in the resources of the
- * application to ensure it is unique. Keys identified as belonging to
+ * application to ensure it is unique (see the <a
+ * href={@docRoot}guide/topics/resources/more-resources.html#Id">ID resource type</a>).
+ * Keys identified as belonging to
* the Android framework or not associated with any package will cause
* an {@link IllegalArgumentException} to be thrown.
*
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 921d0f5..2df250d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -236,14 +236,14 @@ import junit.framework.Assert;
*
* <h3>Building web pages to support different screen densities</h3>
*
- * <p>The screen density of a device is based on the screen resolution. A screen with low density
- * has fewer available pixels per inch, where a screen with high density
- * has more — sometimes significantly more — pixels per inch. The density of a
+ * <p>A screen's density is based on it's screen resolution and physical size. A screen with low
+ * density has fewer available pixels per inch, where a screen with high density
+ * has more -- sometimes significantly more -- pixels per inch. The density of a
* screen is important because, other things being equal, a UI element (such as a button) whose
* height and width are defined in terms of screen pixels will appear larger on the lower density
- * screen and smaller on the higher density screen.
- * For simplicity, Android collapses all actual screen densities into three generalized densities:
- * high, medium, and low.</p>
+ * screen and smaller on the higher density screen. For simplicity, Android collapses all
+ * actual screen densities into three generalized densities:high, medium, and low. </p>
+ *
* <p>By default, WebView scales a web page so that it is drawn at a size that matches the default
* appearance on a medium density screen. So, it applies 1.5x scaling on a high density screen
* (because its pixels are smaller) and 0.75x scaling on a low density screen (because its pixels
diff --git a/core/java/android/widget/BaseExpandableListAdapter.java b/core/java/android/widget/BaseExpandableListAdapter.java
index 396b7ae..b4d6ad7 100644
--- a/core/java/android/widget/BaseExpandableListAdapter.java
+++ b/core/java/android/widget/BaseExpandableListAdapter.java
@@ -43,14 +43,14 @@ public abstract class BaseExpandableListAdapter implements ExpandableListAdapter
}
/**
- * {@see DataSetObservable#notifyInvalidated()}
+ * @see DataSetObservable#notifyInvalidated()
*/
public void notifyDataSetInvalidated() {
mDataSetObservable.notifyInvalidated();
}
/**
- * {@see DataSetObservable#notifyChanged()}
+ * @see DataSetObservable#notifyChanged()
*/
public void notifyDataSetChanged() {
mDataSetObservable.notifyChanged();
diff --git a/core/java/android/widget/HeterogeneousExpandableList.java b/core/java/android/widget/HeterogeneousExpandableList.java
index 1292733..e7e0933 100644
--- a/core/java/android/widget/HeterogeneousExpandableList.java
+++ b/core/java/android/widget/HeterogeneousExpandableList.java
@@ -23,12 +23,13 @@ import android.view.ViewGroup;
* Additional methods that when implemented make an
* {@link ExpandableListAdapter} take advantage of the {@link Adapter} view type
* mechanism.
- *
- * An {@link ExpandableListAdapter} declares one view type for its group items
+ * <p>
+ * An {@link ExpandableListAdapter} declares it has one view type for its group items
* and one view type for its child items. Although adapted for most {@link ExpandableListView}s,
- * these values should be tuned heterogeneous {@link ExpandableListView}s. Lists that contain
- * different types of group and/or child item views, should use an adapter that implements this
- * interface. This way, the recycled views that will be provided to
+ * these values should be tuned for heterogeneous {@link ExpandableListView}s.
+ * </p>
+ * Lists that contain different types of group and/or child item views, should use an adapter that
+ * implements this interface. This way, the recycled views that will be provided to
* {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
* and
* {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
@@ -48,7 +49,7 @@ public interface HeterogeneousExpandableList {
* . Note: Integers must be in the range 0 to {@link #getGroupTypeCount} - 1.
* {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
* @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
- * @see getGroupTypeCount()
+ * @see #getGroupTypeCount()
*/
int getGroupType(int groupPosition);
@@ -65,7 +66,7 @@ public interface HeterogeneousExpandableList {
* Note: Integers must be in the range 0 to {@link #getChildTypeCount} - 1.
* {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
* @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
- * @see getChildTypeCount()
+ * @see #getChildTypeCount()
*/
int getChildType(int groupPosition, int childPosition);
@@ -78,13 +79,11 @@ public interface HeterogeneousExpandableList {
* . If the adapter always returns the same type of View for all group items, this method should
* return 1.
* </p>
- * <p>
* This method will only be called when the adapter is set on the {@link AdapterView}.
- * </p>
*
* @return The number of types of group Views that will be created by this adapter.
- * @see getChildTypeCount()
- * @see getGroupType()
+ * @see #getChildTypeCount()
+ * @see #getGroupType(int)
*/
int getGroupTypeCount();
@@ -97,13 +96,11 @@ public interface HeterogeneousExpandableList {
* , for any group. If the adapter always returns the same type of View for
* all child items, this method should return 1.
* </p>
- * <p>
* This method will only be called when the adapter is set on the {@link AdapterView}.
- * </p>
*
* @return The total number of types of child Views that will be created by this adapter.
- * @see getGroupTypeCount()
- * @see getChildType()
+ * @see #getGroupTypeCount()
+ * @see #getChildType(int, int)
*/
int getChildTypeCount();
}
diff --git a/core/java/com/google/android/mms/ContentType.java b/core/java/com/google/android/mms/ContentType.java
index 94bc9fd..b066fad 100644
--- a/core/java/com/google/android/mms/ContentType.java
+++ b/core/java/com/google/android/mms/ContentType.java
@@ -26,6 +26,7 @@ public class ContentType {
public static final String MMS_GENERIC = "application/vnd.wap.mms-generic";
public static final String MULTIPART_MIXED = "application/vnd.wap.multipart.mixed";
public static final String MULTIPART_RELATED = "application/vnd.wap.multipart.related";
+ public static final String MULTIPART_ALTERNATIVE = "application/vnd.wap.multipart.alternative";
public static final String TEXT_PLAIN = "text/plain";
public static final String TEXT_HTML = "text/html";
diff --git a/core/java/com/google/android/mms/pdu/PduParser.java b/core/java/com/google/android/mms/pdu/PduParser.java
index d465c5a..131ac51 100644
--- a/core/java/com/google/android/mms/pdu/PduParser.java
+++ b/core/java/com/google/android/mms/pdu/PduParser.java
@@ -157,9 +157,11 @@ public class PduParser {
}
String ctTypeStr = new String(contentType);
if (ctTypeStr.equals(ContentType.MULTIPART_MIXED)
- || ctTypeStr.equals(ContentType.MULTIPART_RELATED)) {
+ || ctTypeStr.equals(ContentType.MULTIPART_RELATED)
+ || ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) {
// The MMS content type must be "application/vnd.wap.multipart.mixed"
// or "application/vnd.wap.multipart.related"
+ // or "application/vnd.wap.multipart.alternative"
return retrieveConf;
}
return null;
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index d4ac24a..9fdd204 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -422,7 +422,8 @@ public class PduPersister {
// Store simple string values directly in the database instead of an
// external file. This makes the text searchable and retrieval slightly
// faster.
- if ("text/plain".equals(type) || "application/smil".equals(type)) {
+ if (ContentType.TEXT_PLAIN.equals(type) || ContentType.APP_SMIL.equals(type)
+ || ContentType.TEXT_HTML.equals(type)) {
String text = c.getString(PART_COLUMN_TEXT);
byte [] blob = new EncodedStringValue(text != null ? text : "")
.getTextString();
@@ -736,7 +737,9 @@ public class PduPersister {
try {
byte[] data = part.getData();
- if ("text/plain".equals(contentType) || "application/smil".equals(contentType)) {
+ if (ContentType.TEXT_PLAIN.equals(contentType)
+ || ContentType.APP_SMIL.equals(contentType)
+ || ContentType.TEXT_HTML.equals(contentType)) {
ContentValues cv = new ContentValues();
cv.put(Telephony.Mms.Part.TEXT, new EncodedStringValue(data).getString());
if (mContentResolver.update(uri, cv, null, null) != 1) {
diff --git a/core/jni/android_bluetooth_HeadsetBase.cpp b/core/jni/android_bluetooth_HeadsetBase.cpp
index b0b0cb8..3f14c3a 100644
--- a/core/jni/android_bluetooth_HeadsetBase.cpp
+++ b/core/jni/android_bluetooth_HeadsetBase.cpp
@@ -169,7 +169,7 @@ again:
// never receive non-ASCII UTF-8).
// This was added because of the BMW 2005 E46 which sends binary junk.
if (is_ascii(buf)) {
- LOG(LOG_INFO, "Bluetooth AT recv", buf);
+ IF_LOGV() LOG(LOG_VERBOSE, "Bluetooth AT recv", buf);
} else {
LOGW("Ignoring invalid AT command: %s", buf);
buf[0] = NULL;
@@ -494,7 +494,7 @@ static void pretty_log_urc(const char *urc) {
}
}
}
- LOG(LOG_INFO, "Bluetooth AT sent", buf);
+ IF_LOGV() LOG(LOG_VERBOSE, "Bluetooth AT sent", buf);
free(buf);
}
diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
index 7392ad4..e42b657 100644
--- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
+++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestActivity.java
@@ -24,6 +24,7 @@ import android.content.IntentFilter;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
+import android.view.KeyEvent;
import java.util.List;
import android.widget.LinearLayout;
import android.net.ConnectivityManager;
@@ -64,6 +65,8 @@ public class ConnectivityManagerTestActivity extends Activity {
public int mWifiState;
public NetworkInfo mWifiNetworkInfo;
public String mBssid;
+ public String mPowerSsid = "GoogleGuest"; //Default power SSID
+ private Context mContext;
/*
* Control Wifi States
@@ -404,4 +407,44 @@ public class ConnectivityManagerTestActivity extends Activity {
}
Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
}
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ mContext = this;
+ Bundle bundle = this.getIntent().getExtras();
+ if (bundle != null){
+ mPowerSsid = bundle.getString("power_ssid");
+ }
+ }
+ //A thread to set the device into airplane mode then turn on wifi.
+ Thread setDeviceWifiAndAirplaneThread = new Thread(new Runnable() {
+ public void run() {
+ setAirplaneMode(mContext, true);
+ connectToWifi(mPowerSsid);
+ }
+ });
+
+ //A thread to set the device into wifi
+ Thread setDeviceInWifiOnlyThread = new Thread(new Runnable() {
+ public void run() {
+ connectToWifi(mPowerSsid);
+ }
+ });
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ //This is a tricky way for the scripted monkey to
+ //set the device in wifi and wifi in airplane mode.
+ case KeyEvent.KEYCODE_1:
+ setDeviceWifiAndAirplaneThread.start();
+ break;
+
+ case KeyEvent.KEYCODE_2:
+ setDeviceInWifiOnlyThread.start();
+ break;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index a77717f..036da95 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -36,6 +36,8 @@
android:description="@string/permdesc_testDenied" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
new file mode 100644
index 0000000..0fe83e1
--- /dev/null
+++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java
@@ -0,0 +1,440 @@
+/*
+ * 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.bluetooth;
+
+import android.app.Instrumentation;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+public class BluetoothStressTest extends InstrumentationTestCase {
+ private static final String TAG = "BluetoothEnablerStressTest";
+
+ /**
+ * Timeout for {@link BluetoothAdapter#disable()} in ms.
+ */
+ private static final int DISABLE_TIMEOUT = 5000;
+
+ /**
+ * Timeout for {@link BluetoothAdapter#enable()} in ms.
+ */
+ private static final int ENABLE_TIMEOUT = 20000;
+
+ /**
+ * Timeout for {@link BluetoothAdapter#setScanMode(int)} in ms.
+ */
+ private static final int SET_SCAN_MODE_TIMEOUT = 5000;
+
+ /**
+ * Timeout for {@link BluetoothAdapter#startDiscovery()} in ms.
+ */
+ private static final int START_DISCOVERY_TIMEOUT = 5000;
+
+ /**
+ * Timeout for {@link BluetoothAdapter#cancelDiscovery()} in ms.
+ */
+ private static final int CANCEL_DISCOVERY_TIMEOUT = 5000;
+
+ private static final int DISCOVERY_STARTED_FLAG = 1;
+ private static final int DISCOVERY_FINISHED_FLAG = 1 << 1;
+ private static final int SCAN_MODE_NONE_FLAG = 1 << 2;
+ private static final int SCAN_MODE_CONNECTABLE_FLAG = 1 << 3;
+ private static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG = 1 << 4;
+ private static final int STATE_OFF_FLAG = 1 << 5;
+ private static final int STATE_TURNING_ON_FLAG = 1 << 6;
+ private static final int STATE_ON_FLAG = 1 << 7;
+ private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
+
+ /**
+ * Time between polls in ms.
+ */
+ private static final int POLL_TIME = 100;
+
+ private static final int ENABLE_ITERATIONS = 100;
+ private static final int DISCOVERABLE_ITERATIONS = 1000;
+ private static final int SCAN_ITERATIONS = 1000;
+
+ private Context mContext;
+
+ private Instrumentation mInstrumentation;
+
+ private class BluetoothReceiver extends BroadcastReceiver {
+ private int mFiredFlags = 0;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (this) {
+ if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) {
+ mFiredFlags |= DISCOVERY_STARTED_FLAG;
+ } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) {
+ mFiredFlags |= DISCOVERY_FINISHED_FLAG;
+ } else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(intent.getAction())) {
+ int mode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,
+ BluetoothAdapter.ERROR);
+ assertNotSame(mode, BluetoothAdapter.ERROR);
+ switch (mode) {
+ case BluetoothAdapter.SCAN_MODE_NONE:
+ mFiredFlags |= SCAN_MODE_NONE_FLAG;
+ break;
+ case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
+ mFiredFlags |= SCAN_MODE_CONNECTABLE_FLAG;
+ break;
+ case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
+ mFiredFlags |= SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
+ break;
+ }
+ } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ BluetoothAdapter.ERROR);
+ assertNotSame(state, BluetoothAdapter.ERROR);
+ switch (state) {
+ case BluetoothAdapter.STATE_OFF:
+ mFiredFlags |= STATE_OFF_FLAG;
+ break;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ mFiredFlags |= STATE_TURNING_ON_FLAG;
+ break;
+ case BluetoothAdapter.STATE_ON:
+ mFiredFlags |= STATE_ON_FLAG;
+ break;
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ mFiredFlags |= STATE_TURNING_OFF_FLAG;
+ break;
+ }
+ }
+ }
+ }
+
+ public int getFiredFlags() {
+ synchronized (this) {
+ return mFiredFlags;
+ }
+ }
+
+ public void resetFiredFlags() {
+ synchronized (this) {
+ mFiredFlags = 0;
+ }
+ }
+ }
+
+ private BluetoothReceiver mReceiver = new BluetoothReceiver();
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mInstrumentation = getInstrumentation();
+ mContext = mInstrumentation.getTargetContext();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+ filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
+ filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ mContext.unregisterReceiver(mReceiver);
+ }
+
+ @LargeTest
+ public void testEnableDisable() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+ for (int i = 0; i < ENABLE_ITERATIONS; i++) {
+ Log.i(TAG, "Enable iteration " + (i + 1) + " of " + ENABLE_ITERATIONS);
+ enable(adapter);
+ disable(adapter);
+ }
+ }
+
+ @LargeTest
+ public void testDiscoverable() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ enable(adapter);
+
+ for (int i = 0; i < DISCOVERABLE_ITERATIONS; i++) {
+ Log.i(TAG, "Discoverable iteration " + (i + 1) + " of " + DISCOVERABLE_ITERATIONS);
+ discoverable(adapter);
+ undiscoverable(adapter);
+ }
+
+ disable(adapter);
+ }
+
+ @LargeTest
+ public void testScan() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ enable(adapter);
+
+ for (int i = 0; i < SCAN_ITERATIONS; i++) {
+ Log.i(TAG, "Scan iteration " + (i + 1) + " of " + SCAN_ITERATIONS);
+ startScan(adapter);
+ stopScan(adapter);
+ }
+
+ disable(adapter);
+ }
+
+ private void disable(BluetoothAdapter adapter) {
+ int mask = STATE_TURNING_OFF_FLAG | STATE_OFF_FLAG | SCAN_MODE_NONE_FLAG;
+ mReceiver.resetFiredFlags();
+
+ int state = adapter.getState();
+ switch (state) {
+ case BluetoothAdapter.STATE_OFF:
+ assertFalse(adapter.isEnabled());
+ return;
+ case BluetoothAdapter.STATE_ON:
+ assertTrue(adapter.isEnabled());
+ assertTrue(adapter.disable());
+ break;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ assertFalse(adapter.isEnabled());
+ assertTrue(adapter.disable());
+ break;
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ assertFalse(adapter.isEnabled());
+ mask = 0; // Don't check for received intents since we might have missed them.
+ break;
+ default:
+ fail("disable() invalid state: " + state);
+ }
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < DISABLE_TIMEOUT) {
+ state = adapter.getState();
+ if (state == BluetoothAdapter.STATE_OFF) {
+ assertFalse(adapter.isEnabled());
+ if ((mReceiver.getFiredFlags() & mask) == mask) {
+ mReceiver.resetFiredFlags();
+ return;
+ }
+ } else {
+ assertFalse(adapter.isEnabled());
+ assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state);
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail("disable() timeout: " +
+ "state=" + state + " (expected " + BluetoothAdapter.STATE_OFF + ") " +
+ "flags=" + firedFlags + " (expected " + mask + ")");
+ }
+
+ private void enable(BluetoothAdapter adapter) {
+ int mask = STATE_TURNING_ON_FLAG | STATE_ON_FLAG | SCAN_MODE_CONNECTABLE_FLAG;
+ mReceiver.resetFiredFlags();
+
+ int state = adapter.getState();
+ switch (state) {
+ case BluetoothAdapter.STATE_ON:
+ assertTrue(adapter.isEnabled());
+ return;
+ case BluetoothAdapter.STATE_OFF:
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ assertFalse(adapter.isEnabled());
+ assertTrue(adapter.enable());
+ break;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ assertFalse(adapter.isEnabled());
+ mask = 0; // Don't check for received intents since we might have missed them.
+ break;
+ default:
+ fail("enable() invalid state: state=" + state);
+ }
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < ENABLE_TIMEOUT) {
+ state = adapter.getState();
+ if (state == BluetoothAdapter.STATE_ON) {
+ assertTrue(adapter.isEnabled());
+ if ((mReceiver.getFiredFlags() & mask) == mask) {
+ mReceiver.resetFiredFlags();
+ return;
+ }
+ } else {
+ assertFalse(adapter.isEnabled());
+ assertEquals(BluetoothAdapter.STATE_TURNING_ON, state);
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail("enable() timeout: " +
+ "state=" + state + " (expected " + BluetoothAdapter.STATE_OFF + ") " +
+ "flags=" + firedFlags + " (expected " + mask + ")");
+ }
+
+ private void discoverable(BluetoothAdapter adapter) {
+ int mask = SCAN_MODE_CONNECTABLE_DISCOVERABLE_FLAG;
+ mReceiver.resetFiredFlags();
+
+ if (!adapter.isEnabled()) {
+ fail("discoverable() bluetooth not enabled");
+ }
+
+ int scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+ return;
+ }
+
+ assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE));
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+ scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
+ if ((mReceiver.getFiredFlags() & mask) == mask) {
+ mReceiver.resetFiredFlags();
+ return;
+ }
+ } else {
+ assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail("discoverable() timeout: " +
+ "scanMode=" + scanMode + " (expected " +
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE + ") " +
+ "flags=" + firedFlags + " (expected " + mask + ")");
+ }
+
+ private void undiscoverable(BluetoothAdapter adapter) {
+ int mask = SCAN_MODE_CONNECTABLE_FLAG;
+ mReceiver.resetFiredFlags();
+
+ if (!adapter.isEnabled()) {
+ fail("undiscoverable(): bluetooth not enabled");
+ }
+
+ int scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
+ return;
+ }
+
+ assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ assertTrue(adapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE));
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < SET_SCAN_MODE_TIMEOUT) {
+ scanMode = adapter.getScanMode();
+ if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE) {
+ if ((mReceiver.getFiredFlags() & mask) == mask) {
+ mReceiver.resetFiredFlags();
+ return;
+ }
+ } else {
+ assertEquals(scanMode, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail("undiscoverable() timeout: " +
+ "scanMode=" + scanMode + " (expected " +
+ BluetoothAdapter.SCAN_MODE_CONNECTABLE + ") " +
+ "flags=" + firedFlags + " (expected " + mask + ")");
+ }
+
+ private void startScan(BluetoothAdapter adapter) {
+ int mask = DISCOVERY_STARTED_FLAG;
+ mReceiver.resetFiredFlags();
+
+ if (!adapter.isEnabled()) {
+ fail("startScan(): bluetooth not enabled");
+ }
+
+ if (adapter.isDiscovering()) {
+ return;
+ }
+
+ assertTrue(adapter.startDiscovery());
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < START_DISCOVERY_TIMEOUT) {
+ if (adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
+ mReceiver.resetFiredFlags();
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail("startScan() timeout: " +
+ "isDiscovering=" + adapter.isDiscovering() + " " +
+ "flags=" + firedFlags + " (expected " + mask + ")");
+ }
+
+ private void stopScan(BluetoothAdapter adapter) {
+ int mask = DISCOVERY_FINISHED_FLAG;
+ mReceiver.resetFiredFlags();
+
+ if (!adapter.isEnabled()) {
+ fail("stopScan(): bluetooth not enabled");
+ }
+
+ if (!adapter.isDiscovering()) {
+ return;
+ }
+
+ // TODO: put assertTrue() around cancelDiscovery() once it starts
+ // returning true.
+ adapter.cancelDiscovery();
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < CANCEL_DISCOVERY_TIMEOUT) {
+ if (!adapter.isDiscovering() && ((mReceiver.getFiredFlags() & mask) == mask)) {
+ mReceiver.resetFiredFlags();
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = mReceiver.getFiredFlags();
+ mReceiver.resetFiredFlags();
+ fail("stopScan() timeout: " +
+ "isDiscovering=" + adapter.isDiscovering() + " " +
+ "flags=" + firedFlags + " (expected " + mask + ")");
+ }
+
+ private void sleep(long time) {
+ try {
+ Thread.sleep(time);
+ } catch (InterruptedException e) {
+ }
+ }
+}
diff --git a/docs/html/guide/developing/eclipse-adt.jd b/docs/html/guide/developing/eclipse-adt.jd
index cf2a457..9c77ece 100644
--- a/docs/html/guide/developing/eclipse-adt.jd
+++ b/docs/html/guide/developing/eclipse-adt.jd
@@ -527,7 +527,7 @@ Marking a project as an Android library project. </p>
<p>A library project's manifest file must declare all of the shared components
that it includes, just as would a standard Android application. For more
information, see the documentation for <a
-href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
<p>For example, the <a
href="{@docRoot}resources/samples/TicTacToeLib/AndroidManifest.html">TicTacToeLib</a>
@@ -613,7 +613,8 @@ like this: </p>
...
&lt;/manifest&gt;</pre>
-<p>For more information about the manifest file, see the documentation for <a href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
+<p>For more information about the manifest file, see the documentation for <a
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
<h3 id="considerations">Development considerations</h3>
@@ -784,6 +785,8 @@ DDMS and configure Eclipse to debug on port 8700. (<strong>Note: </strong>Be sur
have first started <a href="{@docRoot}guide/developing/tools/ddms.html">DDMS</a>). </p>
+
+
<!-- TODO: clean this up and expand it to cover more wizards and features
<h3>ADT Wizards</h3>
diff --git a/docs/html/guide/developing/other-ide.jd b/docs/html/guide/developing/other-ide.jd
index e8a6fb6..1d67aa9 100644
--- a/docs/html/guide/developing/other-ide.jd
+++ b/docs/html/guide/developing/other-ide.jd
@@ -687,7 +687,7 @@ so that other applications can use it, you can do so by adding a the
<p>A library project's manifest file must declare all of the shared components
that it includes, just as would a standard Android application. For more
information, see the documentation for <a
-href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
<p>For example, the <a
href="{@docRoot}resources/samples/TicTacToeLib/AndroidManifest.html">TicTacToeLib</a>
@@ -799,7 +799,8 @@ like this: </p>
...
&lt;/manifest&gt;</pre>
-<p>For more information about the manifest file, see the documentation for <a href="{@docRoot}guide/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
+<p>For more information about the manifest file, see the documentation for <a
+href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a>.</p>
<h3 id="depAppBuild">Building a dependent application</h3>
diff --git a/docs/html/guide/developing/tools/avd.jd b/docs/html/guide/developing/tools/avd.jd
index acebb7e..ca197cf 100644
--- a/docs/html/guide/developing/tools/avd.jd
+++ b/docs/html/guide/developing/tools/avd.jd
@@ -11,16 +11,13 @@ page.title=Android Virtual Devices
hardware options, system image, and data storage.
<li>You create AVD configurations to model different device environments
in the Android emulator.</li>
- <li>The <code>android</code> tool offers a graphical Android AVD
- Manager and a command-line interface for creating AVDs.</li>
- </ul>
+ <li>You can launch a graphical Android AVD Manager either through Eclipse or
+through the <code>android</code> tool. The <code>android</code> tool also offers
+a command-line interface for creating and managing AVDs.</li> </ul>
<h2>In this document</h2>
<ol>
<li><a href="#creating">Creating an AVD</a>
<ol>
- <li><a href="#listingtargets">Listing targets</a></li>
- <li><a href="#selectingtarget">Selecting a target</a></li>
- <li><a href="#createavd">Creating the AVD</a></li>
<li><a href="#hardwareopts">Setting hardware emulation options</a></li>
<li><a href="#location">Default location of the AVD files</a></li>
</ol>
@@ -74,15 +71,12 @@ reference of emulator options, please see the <a
href="{@docRoot}guide/developing/tools/emulator.html">Emulator</a>
documentation. </p>
-<p>To create and manage AVDs, you use the <code>android</code> tool provided in
-the <code>tools/</code> directory of the Android SDK. The tool provides both a
-graphical AVD manager and a command-line interface that you can use to
-create AVDs. To access the graphical AVD manager, run the
-<code>android</code> tool without options. The sections below describe how to
-use the <code>android</code> command-line interface to create and manage AVDs.
-Note that some functionality, such as the capability to create an AVD with a
-custom hardware configuration, are only available through the command-line
-interface. </p>
+<p>The easiest way to create an AVD is to use the graphical AVD Manager, which
+you can launch from Eclipse or from the command line using the
+<code>android</code> tool. The <code>android</code> tool is provided in the
+<code>tools/</code> directory of the Android SDK. When you run the
+<code>android</code> tool without options, it launches the graphical AVD
+Manager.</p>
<p>For more information about how to work with AVDs from inside your development
environment, see <a
@@ -99,146 +93,51 @@ you need to create an AVD before you can run any application in the emulator
(even the Hello World application).</p>
</div>
</div>
-
-<p>To create an AVD, you use the <code>android</code> tool, a command-line
-utility available in the <code>&lt;sdk&gt;/tools/</code> directory. Managing
-AVDs is one of the two main function of the <code>android</code> tool (the other
-is creating and updating Android projects). Open a terminal window and change to
-the <code>&lt;sdk&gt;/tools/</code> directory, if needed</p>
-
-<p>To create each AVD, you issue the command <code>android create avd</code>,
-with options that specify a name for the new AVD and the system image you want
-to run on the emulator when the AVD is invoked. You can specify other options on
-the command line also, such as to create an emulated SD card for the new AVD, set
-the emulator skin to use, or set a custom location for the AVD's files.</p>
-
-<p>Here's the command-line usage for creating an AVD: </p>
-
-<pre>android create avd -n &lt;name&gt; -t &lt;targetID&gt; [-&lt;option&gt; &lt;value&gt;] ... </pre>
-
-<p>You can use any name you want for the AVD, but since you are likely to be
-creating multiple AVDs, you should choose a name that lets you recognize the
-general characteristics offered by the AVD. </p>
-
-<p>As shown in the usage above, you must use the <code>-t</code> (or
-<code>--target</code>) argument when creating a new AVD. The argument sets up a
-mapping between the AVD and the system image that you want to use whenever the
-AVD is invoked. You can specify any Android system image that is available in
-your local SDK &mdash; it can be the system image of a standard Android platform
-version or that of any SDK add-on. Later, when applications use the AVD, they'll
-be running on the system that you specify in the <code>-t</code> argument.<p>
-
-<p>To specify the system image to use, you refer to its <em>target ID</em>
-&mdash; an integer &mdash; as assigned by the <code>android</code> tool. The
-target ID is not derived from the system image name, version, or API Level, or
-other attribute, so you need to have the <code>android</code> tool list the
-available system images and the target ID of each, as described in the next
-section. You should do this <em>before</em> you run the <code>android create
-avd</code> command.
-</p>
-
-<h3 id="listingtargets">Listing targets</h3>
-
-<p>To generate a list of system image targets, use this command: </p>
-
-<pre>android list targets</pre>
-
-<p>The <code>android</code> tool scans the <code>&lt;sdk&gt;/platforms</code> and
-<code>&lt;sdk&gt;/add-ons</code> directories looking for valid system images and
-then generates the list of targets. Here's an example of the command output:
-</p>
-
-<pre>Available Android targets:
-id:1
- Name: Android 1.1
- Type: platform
- API level: 2
- Skins: HVGA (default), HVGA-L, HVGA-P, QVGA-L, QVGA-P
-id:2
- Name: Android 1.5
- Type: platform
- API level: 3
- Skins: HVGA (default), HVGA-L, HVGA-P, QVGA-L, QVGA-P
-id:3
- Name: Google APIs
- Type: add-on
- Vendor: Google Inc.
- Description: Android + Google APIs
- Based on Android 1.5 (API level 3)
- Libraries:
- * com.google.android.maps (maps.jar)
- API for Google Maps
- Skins: HVGA (default), HVGA-L, QVGA-P, HVGA-P, QVGA-L</pre>
-
-<h3 id="selectingtarget">Selecting a target</h3>
-
-<p>Once you have generated the list of targets available, you can look at the
-characteristics of each system image &mdash; name, API Level, external
-libraries, and so on &mdash; and determine which target is appropriate for the
-new AVD. </p>
-
-<p>Keep these points in mind when you are selecting a system image target for
-your AVD:</p>
-<ul>
-<li>The API Level of the target is important, because your application will not
-be able to run on a system image whose API Level is less than that required by
-your application, as specified in the <code>minSdkVersion</code> attribute of
-the application's manifest file. For more information about the relationship
-between system API Level and application <code>minSdkVersion</code>, see <a
-href="{@docRoot}guide/publishing/versioning.html#minsdkversion">Specifying
-Minimum System API Version</a>.
-<li>Creating at least one AVD that uses a target whose API Level is greater than
-that required by your application is strongly encouraged, because it allows you to
-test the forward-compatibility of your application. Forward-compatibility
-testing ensures that, when users who have downloaded your application receive a
-system update, your application will continue to function normally. </li>
-<li>If your application declares a <code>uses-library</code> element in its
-manifest file, the application can only run on a system image in which that
-external library is present. If you want your application to run on the AVD you
-are creating, check the application's <code>uses-library</code> element and
-select a system image target that includes that library.
-
-</ul>
-
-<h3 id="createavd">Creating the AVD</h3>
-
-<p>When you've selected the target you want to use and made a note of its ID,
-use the <code>android create avd</code> command to create the AVD, supplying the
-target ID as the <code>-t</code> argument. Here's an example that creates an
-AVD with name "my_android1.5" and target ID "2" (the standard Android 1.5
-system image in the list above): </p>
-
-<pre>android create avd -n my_android1.5 -t 2</pre>
-
-<p>If the target you selected was a standard Android system image ("Type:
-platform"), the <code>android</code> tool next asks you whether you want to
-create a custom hardware profile. </p>
-<pre>Android 1.5 is a basic Android platform.
-Do you wish to create a custom hardware profile [no]</pre>
-
-<p>If you want to set custom hardware emulation options for the AVD, enter
-"yes" and set values as needed. If you want to use the default hardware
-emulation options for the AVD, just press the return key (the default is "no").
-The <code>android</code> tool creates the AVD with name and system image mapping you
-requested, with the options you specified.
-
-<p class="note">If you are creating an AVD whose target is an SDK add-on, the
-<code>android</code> tool does not allow you to set hardware emulation options.
-It assumes that the provider of the add-on has set emulation options
-appropriately for the device that the add-on is modeling, and so prevents you
-from resetting the options. </p>
-<p>For a list of options you can use in the <code>android create avd</code>
-command, see the table in <a href="#options">Command-line options for AVDs</a>,
-at the bottom of
-this page. </p>
+<p>The easiest way to create an AVD is to use the graphical AVD Manager, but the
+<code>android</code> tool also offers a <a href="#options">command line option</a>.</p>
+<p>To create an AVD:</p>
+<ol>
+ <li>In Eclipse, choose <strong>Window &gt; Android SDK and AVD Manager</strong>. </li>
+ <p>Alternatively, you can launch the graphical AVD Manager by running the
+<code>android</code> tool with no options.</p>
+ <li>Select <strong>Virtual Devices</strong> in the left panel.</li>
+
+ <li>Click <strong>New</strong>. </li>
+
+<p>The <strong>Create New AVD</strong> dialog appears.</p> <a
+href="{@docRoot}images/developing/avd-dialog.png"><img
+src="{@docRoot}images/developing/avd-dialog.png" alt="AVD
+Dialog" /></a>
+
+ <li>Type the name of the AVD, such as "my_avd".</li>
+ <li>Choose a target. </li>
+<p>The target is the system image that you want to run on the emulator,
+from the set of platforms that are installed in your SDK environment. You can
+choose a version of the standard Android platform or an SDK add-on. For more
+information about how to add platforms to your SDK, see <a
+href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>. </p>
+ <li>Optionally specify any additional settings: </li>
+ <dl>
+ <dt><em>SD Card</em></dt> <dd>The path to the SD card image to use with this
+AVD, or the size of a new SD card image to create for this AVD.</dd> </dl>
+<dt><em>Skin</em></dt>
+ <dd>The skin to use for this AVD, identified by name or dimensions.</dd>
+<dt><em>Hardware</em></dt>
+ <dd>The hardware emulation options for the device. For a list of the options, see
+<a href="#hardwareopts">Setting hardware emulation options</a>.</dd>
+ </dl>
+ <li>Click <strong>Create AVD</strong>.</li>
+</ol>
<h3 id="hardwareopts">Setting hardware emulation options</h3>
-<p>When are creating a new AVD that uses a standard Android system image ("Type:
-platform"), the <code>android</code> tool lets you set hardware emulation
-options for virtual device. The table below lists the options available and the
+<p>When you create a new AVD that uses a standard Android system image ("Type:
+platform"), the AVD Manager
+ lets you set hardware emulation
+options for your virtual device.
+The table below lists the options available and the
default values, as well as the names of properties that store the emulated
-hardware options in the AVD's configuration file (the config.ini file in the
+hardware options in the AVD's configuration file (the <code>config.ini</code> file in the
AVD's local directory). </p>
<table>
@@ -266,6 +165,7 @@ AVD's local directory). </p>
</tr>
<tr>
+
<td>Keyboard support</td>
<td>Whether the device has a QWERTY keyboard. Default value is "yes".</td>
<td>hw.keyboard</td>
@@ -299,6 +199,7 @@ AVD's local directory). </p>
<td>Maximum vertical camera pixels</td>
<td>Default value is "480".</td>
<td>hw.camera.maxVerticalPixels</td>
+
</tr>
<tr>
@@ -311,6 +212,7 @@ AVD's local directory). </p>
<td>Battery support</td>
<td>Whether the device can run on a battery. Default value is "yes".</td>
<td>hw.battery</td>
+
</tr>
<tr>
@@ -323,6 +225,7 @@ AVD's local directory). </p>
<td>Audio recording support</td>
<td>Whether the device can record audio. Default value is "yes".</td>
<td>hw.audioInput</td>
+
</tr>
<tr>
@@ -335,6 +238,7 @@ AVD's local directory). </p>
<td>SD Card support</td>
<td>Whether the device supports insertion/removal of virtual SD Cards. Default value is "yes".</td>
<td>hw.sdCard</td>
+
</tr>
<tr>
@@ -347,43 +251,55 @@ AVD's local directory). </p>
<td>Cache partition size</td>
<td>Default value is "66MB".</td>
<td>disk.cachePartition.size </td>
+
</tr>
<tr>
<td>Abstracted LCD density</td>
-<td>Sets the generalized density characteristic used by the AVD's screen. Default value is "160".</td>
+<td>Sets the generalized density characteristic used by the AVD's screen. Most
+skins come with a value (which you can modify), but if a skin doesn't provide
+its own value, the default is 160. </td>
<td>hw.lcd.density </td>
</tr>
+<tr>
+<td>Max VM application heap size</td>
+<td>The maximum heap size a Dalvik application might allocate before being
+killed by the system. Value is in megabytes. Most skins come with a value (which
+you can modify), but if a skin doesn't provide its own value, the default is
+16.</td>
+<td>vm.heapSize</td>
+</tr>
+
</table>
<h3 id="location">Default location of the AVD files</h3>
-<p>When you create an AVD, the <code>android</code> tool creates a dedicated directory for it
+<p>When you create an AVD, the AVD Manager creates a dedicated directory for it
on your development computer. The directory contains the AVD configuration file,
the user data image and SD card image (if available), and any other files
associated with the device. Note that the directory does not contain a system
image &mdash; instead, the AVD configuration file contains a mapping to the
system image, which it loads when the AVD is launched. </p>
-<p>The <code>android</code> tool also creates a &lt;AVD name&gt;.ini file for the AVD at the
-root of the .android/avd directory on your computer. The file specifies the
-location of the AVD directory and always remains at the root the .android
-directory.</p>
+<p>The AVD Manager also creates a <code>&lt;AVD name&gt;.ini</code> file for the
+AVD at the root of the <code>.android/avd</code> directory on your computer. The file
+specifies the location of the AVD directory and always remains at the root the
+.android directory.</p>
-<p>By default, the <code>android</code> tool creates the AVD directory inside
+<p>By default, the AVD Manager creates the AVD directory inside
<code>~/.android/avd/</code> (on Linux/Mac), <code>C:\Documents and
Settings\&lt;user&gt;\.android\</code> on Windows XP, and
<code>C:\Users\&lt;user&gt;\.android\</code> on Windows Vista.
If you want to use a custom location for the AVD directory, you
can do so by using the <code>-p &lt;path&gt;</code> option when
-you create the AVD: </p>
+you create the AVD (command line tool only): </p>
<pre>android create avd -n my_android1.5 -t 2 -p path/to/my/avd</pre>
-<p>If the .android directory is hosted on a network drive, we recommend using
+<p>If the <code>.android</code> directory is hosted on a network drive, we recommend using
the <code>-p</code> option to place the AVD directory in another location.
-The AVD's .ini file remains in the .android directory on the network
+The AVD's <code>.ini</code> file remains in the <code>.android</code> directory on the network
drive, regardless of the location of the AVD directory. </p>
<h2 id="managing">Managing AVDs</h2>
@@ -401,18 +317,15 @@ options for AVDs</a> at the bottom of this page. </p>
<h3 id="updating">Updating an AVD</h3>
-<p>If, for any reason, the platform/add-on root folder has its name changed (maybe because the user has installed an update of the platform/add-on) then the AVD will not be able to load the system image that it is mapped to. In this case, the <code>android list targets</code> command will produce this output:
-
-<pre>The following Android Virtual Devices could not be loaded:
-Name: foo
-Path: &lt;path&gt;/.android/avd/foo.avd
-Error: Invalid value in image.sysdir. Run 'android update avd -n foo' </pre>
-
-<p>To fix this error, use the <code>android update avd</code> command to recompute the path to the system images.</p>
+<p>
+If you rename or move the root directory of a platform (or add-on), an AVD configured to use that platform will no longer be able to load the system image properly. To fix the AVD, use the <strong>Repair...</strong> button in the AVD Manager. From the command line, you can also use the <code>android update avd</code> command to recompute the path to the system images.</p>
<h3 id="deleting">Deleting an AVD</h3>
-<p>You can use the <code>android</code> tool to delete an AVD. Here is the command usage:</p>
+<p>You can delete an AVD in the AVD Manager by selecting the
+AVD and clicking <strong>Delete</strong>.</p>
+
+<p>Alternatively, you can use the <code>android</code> tool to delete an AVD. Here is the command usage:</p>
<pre>android delete avd -n &lt;name&gt; </pre>
@@ -420,7 +333,21 @@ Error: Invalid value in image.sysdir. Run 'android update avd -n foo' </pre>
specified name deletes the AVD's directory and files. </p>
-<h2 id="options">Command-line options for AVDs</h2>
+<h2 id="options">Command-line options</h2>
+
+<p>You can use the <code>android</code> tool to create and manage AVDs.</p>
+
+<p>The command line for creating an AVD has the following syntax:</p>
+
+<pre>
+android create avd -n &lt;name&gt; -t &lt;targetID&gt; [-&lt;option&gt; &lt;value&gt;] ...
+</pre>
+
+<p>Here's an example that creates an AVD with the name "my_android2.2" and target ID "3":</p>
+
+<pre>
+android create avd -n my_android2.2 -t 3
+</pre>
<p>The table below lists the command-line options you can use with the
<code>android</code> tool. </p>
diff --git a/docs/html/guide/developing/tools/bmgr.jd b/docs/html/guide/developing/tools/bmgr.jd
index 2f49532..57deb25 100644
--- a/docs/html/guide/developing/tools/bmgr.jd
+++ b/docs/html/guide/developing/tools/bmgr.jd
@@ -15,6 +15,11 @@ page.title=bmgr
<li><a href="#other">Other Commands</a></li>
</ol>
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a></li>
+ </ol>
+
</div>
</div>
@@ -26,6 +31,9 @@ and restore operations so that you don't need to repeatedly wipe data or take si
intrusive steps in order to test your application's backup agent. These commands are
accessed via the <a href="{@docRoot}guide/developing/tools/adb.html">adb</a> shell.
+<p>For information about adding support for backup in your application, read <a
+href="{@docRoot}guide/topics/data/backup.html">Data Backup</a>, which includes a guide to testing
+your application using {@code bmgr}.</p>
<h2 id="backup">Forcing a Backup Operation</h2>
@@ -90,6 +98,8 @@ will happen even if your application is not currently running.
<h2 id="other">Other Commands</h2>
+<h3>Wiping data</h3>
+
<p>The data for a single application can be erased from the active data set on demand. This is
very useful while you're developing a backup agent, in case bugs lead you to write corrupt data
or saved state information. You can wipe an application's data with the <code>bmgr wipe</code>
@@ -102,6 +112,9 @@ you wish to
erase. The next backup operation that the application's agent processes will look as
though the application had never backed anything up before.
+
+<h3>Enabling and disabling backup</h3>
+
<p>You can see whether the Backup Manager is operational at all with the <code>bmgr
enabled</code> command:
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 19f0f1d..d0318cf 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -371,6 +371,9 @@
<span class="zh-CN" style="display:none">应用程序版本控制</span>
<span class="zh-TW" style="display:none">應用程式版本設定</span>
</a></li>
+ <li><a href="<?cs var:toroot ?>guide/publishing/licensing.html">
+ <span class="en">Licensing Your Applications</span>
+ </a> <span class="new">new!</span></li>
<li><a href="<?cs var:toroot ?>guide/publishing/preparing.html">
<span class="en">Preparing to Publish</span>
<span class="de" style="display:none">Vorbereitung auf die Veröffentlichung</span>
diff --git a/docs/html/guide/practices/ui_guidelines/activity_task_design.jd b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
index 05f61be..c8d241c 100644
--- a/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
+++ b/docs/html/guide/practices/ui_guidelines/activity_task_design.jd
@@ -256,8 +256,8 @@ page.title=Activity and Task Design Guidelines
to be able go to a subsequent screen B and then use the BACK key to go
back to screen A, then the screen A needs to be implemented as an
activity. The one exception to this rule is if your application
- <a href=#taking_over_back_key title="takes control of the BACK key"
- takes control of the BACK key</a> and manages the navigation itself.
+ <a href="#taking_over_back_key">takes control of the BACK key</a> and manages the navigation
+itself.
</p>
diff --git a/docs/html/guide/publishing/licensing.jd b/docs/html/guide/publishing/licensing.jd
new file mode 100644
index 0000000..07af68d
--- /dev/null
+++ b/docs/html/guide/publishing/licensing.jd
@@ -0,0 +1,2376 @@
+page.title=Licensing Your Applications
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>Market Licensing quickview: </h2>
+ <ul>
+ <li>Licensing lets you protect your application on any device that includes Android Market.</li>
+ <li>Your app maintains control of how it enforces its licensing status. </li>
+ <li>Adding licensing to an app is straightforward, using the library available through the SDK.</li>
+ <li>The service is free and is available to all developers who publish on Android Market. </li>
+ </ul>
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#account">Setting Up A Publisher Account</a></li>
+ <li><a href="#dev-setup">Setting Up the Development Environment</a></li>
+ <li><a href="#app-integration">Integrating the LVL with Your Application</a>
+ <ol>
+ <li><a href="#add-library">Including the LVL</a></li>
+ <li><a href="#manifest-permission">Adding the licensing permission</a></li>
+ <li><a href="#impl-Policy">Implementing a Policy</a></li>
+ <li><a href="#impl-Obfuscator">Implementing an Obfuscator</a></li>
+ <li><a href="#impl-lc">Checking the license</a></li>
+ <li><a href="#impl-DeviceLimiter">Implementing a DeviceLimiter</a></li>
+ </ol></li>
+ <li><a href="#test-env">Setting Up the Test Environment</a>
+ <ol>
+ <li><a href="#test-response">Test responses</a></li>
+ <li><a href="#test-acct-setup">Test accounts</a></li>
+ <li><a href="#acct-signin">Signing in on a device or emulator</a></li>
+ </ol></li>
+ <li><a href="#app-obfuscation">Obfuscating Your Application</a></li>
+ <li><a href="#app-publishing">Publishing a Licensed Application</a></li>
+ <li><a href="#support">Where to Get Support</a></li>
+ </ol>
+
+ <h2>Appendix</h2>
+ <ol>
+ <li><a href="#lvl-summary">Summary of LVL Classes and Interfaces</a></li>
+ <li><a href="#server-response-codes">Server Response Codes</a></li>
+ <li><a href="#extras">Server Response Extras</a></li>
+ </ol>
+
+</div>
+</div>
+
+<p>Android Market offers a licensing service that lets you enforce licensing
+policies for paid applications that you publish through Android Market. With
+Android Market Licensing, your applications can query Android Market at run time to
+obtain their licensing status for the current user, then allow or disallow
+further use as appropriate. </p>
+
+<p>Using the service, you can apply a flexible licensing policy on an
+application-by-application basis &mdash; each application can enforce licensing
+in the way most appropriate for it. If necessary, an application can apply custom
+constraints based on the licensing status obtained from Android Market.
+For example, an application can check the licensing status and then apply custom
+constraints that allow the user to run it unlicensed for a specific number
+of times, or for a specific validity period. An application can also restrict use of the
+application to a specific device, in addition to any other constraints. </p>
+
+<p>The licensing service is a secure means of controlling access to your
+applications. When an application checks the licensing status, the Market server
+signs the licensing status response using a key pair that is uniquely associated
+with the publisher account. Your application stores the public key in its
+compiled <code>.apk</code> file and uses it to verify the licensing status
+response.</p>
+
+<p>Any application that you publish through Android Market can use the Android
+Market Licensing service. No special account or registration is needed.
+Additionally, because the service uses no dedicated framework APIs, you can add
+licensing to any legacy application that uses a minimum API level of 3 or
+higher.</p>
+
+<p>To help you add licensing to your application, the Android SDK provides
+library sources that you can include in your application project. The
+License Verification Library (LVL) handles all of
+the licensing-related communication with the Android Market client and the
+licensing service. With the LVL integrated, your application can determine its
+licensing status for the current user by simply calling a library checker method
+and implementing a callback that receives the status.</p>
+
+<p>This document explains how the licensing service works and how to add it to
+your application. </p>
+
+
+<h2 id="overview">Overview</h2>
+
+<p>Android Market Licensing is a network-based service that lets an application
+on an Android-powered device query a trusted licensing server, to determine
+whether the application is licensed to the current device user. After receiving
+the server response, the application can then allow or disallow further use of
+the application as needed. In the service, the role of the licensing server is
+to provide the license status for the current user; the application itself is
+responsible for querying the server and conditionally granting access to the
+application. </p>
+
+<h4>Application, Android Market client, and server</h4>
+
+<p>The licensing service is based on the capability of the Android Market server
+to determine whether a given user is licensed to use a given application. The
+server considers a user licensed if the user is recorded to have purchased the
+application, or if the application is available for free. To properly identify
+the user and determine the license status, the server requires information about
+the application and user &mdash; the application and the Android Market client
+work together to assemble the information and pass it to the server. </p>
+
+<p>In the licensing service, an application does not query the licensing server
+directly, but instead calls the Android Market client over remote IPC to
+initiate a license request. In the license request:</p>
+
+<ul>
+<li>The application provides its package name and a nonce that is later used to
+validate any response from the server, as well as a callback over which the
+response can be returned asynchronously.</li>
+<li>The Android Market client, which has greater permissions than the
+application, collects the necessary information about the user and the device,
+such as the device's primary Google account username, IMSI, and other
+information. It then sends the license check request to the server on behalf of
+the application.</li>
+<li>The server evaluates the request using all available information, attempting
+to establish the user's identity to a sufficient level of confidence. The server
+then checks the user identity against purchase records for the application and
+returns a license response, which the Android Market client returns to the
+application over the IPC callback.</li>
+</ul>
+
+<p>Notice that during a license check, the application does not manage any
+network connections or use any licensing related APIs in the Android platform.
+</p>
+
+<div class="figure" style="width:469px">
+<img src="{@docRoot}images/licensing_arch.png" alt=""/>
+<p class="img-caption"><strong>Figure 1.</strong> Your application initiates a
+license check through the LVL and the Android Market
+client, which handles communication with the Market server.</p>
+</div>
+
+<h4>License responses secured through public key cryptography</h4>
+
+<p>To ensure the integrity of each license query, the server signs the license
+response data using an RSA key pair that is shared exclusively between the
+server and the application publisher.</p>
+
+<p>The licensing service generates a single licensing key pair for each
+publisher account and exposes the public key in the account's profile page. The
+publisher copies the public key and embeds it in the application source code,
+then compiles and publishes the <code>.apk.</code> The server retains the
+private key internally and uses it to sign license responses for applications
+published on that account. </p>
+
+<p>When the application receives a signed response, it uses the embedded public
+key to verify the data. The use of public key cryptography in the licensing
+service makes it possible for the application to detect responses that have been
+tampered with or that are spoofed.</p>
+
+<h4>Use of licensing in your application</h4>
+
+<p>To use licensing in your application, add code to the application to
+initiate a license check request and handle the response when it is received.
+You can choose when, and how often, you want your application to check its
+license and you have full control over how it handles the response, verifies the
+signed response data, and enforces access controls. </p>
+
+<p>To simplify the process of adding support for licensing, download and
+integrate the Licensing Verification Library, described below. Integration is
+straightforward.</p>
+
+<p>When you are finished integrating the LVL, use a test environment
+provided by the publisher site to test your application's handling of server
+responses. </p>
+
+<p>Finally, publish the application <code>.apk</code> on Market using the
+normal process. If you previously used the copy-protection provided by Android
+Market, you can remove it from applications that use licensing. </p>
+
+<h4>Licensing Verification Library simplifies implementation</h4>
+
+<p>The Android SDK includes a License Verification Library (LVL) that you can
+download and use as the basis for your application's licensing implementation.
+The LVL greatly simplifies the process of adding licensing to your application
+and helps ensure a more secure, robust implementation for your application. The
+LVL provides internal classes that handle most of the standard operations of a
+license query, such as contacting Android Market to initiate a license request
+and verifying and validating the responses. It also exposes key interfaces that
+let you easily plug in your custom code for defining licensing policy and
+managing access as needed by your application. The key LVL interfaces are: </p>
+
+<ul>
+<li>Policy &mdash; your implementation determines whether to allow access to the
+application, based on the license response received from the server and any
+other data available (such as from a backend server associated with your
+application). The implementation can evaluate the various fields of the license
+response and apply other constraints, if needed. The implementation also lets
+you manage the handling of license checks that result in errors, such as network
+errors.</li>
+<li>LicenseCheckerCallback &mdash; your implementation manages access to the
+application, based on the result of the Policy's handling of the license
+response. Your implementation can manage access in any way needed, including
+displaying the license result in the UI or directing the user to purchase the
+application (if not currently licensed). </li>
+</ul>
+
+<p>To help you get started with a Policy, the LVL provides two fully complete
+Policy implementations that you can use without modification or adapt to your
+needs:</p>
+
+<ul>
+<li><a href="#ServerManagedPolicy">ServerManagedPolicy</a> is a flexible Policy
+that uses settings provided by the licensing server to manage response caching
+and access to the application while the device is offline (such as when the
+user is on an airplane). For most applications, the use of
+ServerManagedPolicy is highly recommended. </li>
+<li><a href="#StrictPolicy">StrictPolicy</a> is a restrictive Policy that
+does not cache any response data and allows the application access <em>only</em>
+when the server returns a licensed response.</li>
+</ul>
+
+<p>The LVL is available as a downloadable component of the Android SDK. The
+component includes both the LVL itself and an example application that shows how
+the library should be integrated with your application and how your application
+should manage response data, UI interaction, and error conditions. </p>
+
+<p>The LVL sources are provided as an Android <em>library project</em>, which
+means that you can maintain a single set of library sources and share them
+across multiple applications. A full test environment is also available through
+the SDK, so you can develop and test the licensing implementation in your
+applications before publishing them, even if you don't have access to a
+physical device.</p>
+
+<h4>Requirements and limitations</h4>
+
+<p>Android Market Licensing is designed to let you apply license controls to
+applications that you publish through Android Market. The service is not
+designed to let you control access to applications that are not published
+through Android Market or that are run on devices that do not offer the Android
+Market client. </p>
+
+<p>Here are some points to keep in mind as you implement licensing in your
+application: </p>
+
+<ul>
+<li>Only paid applications published through Market can use the
+service. </li>
+<li>An application can use the service only if the Android Market client is
+installed on its host device and the device is running Android 1.5 (API level 3)
+or higher.</li>
+<li>To complete a license check, the licensing server must be accessible over
+the network. You can implement license caching behaviors to manage access when
+there is no network connectivity. </li>
+<li>The security of your application's licensing controls ultimately relies on
+the design of your implementation itself. The service provides the building
+blocks that let you securely check licensing, but the actual enforcement and
+handling of the license are factors in your control. By following the best
+practices in this document, you can help ensure that your implementation will be
+secure.</li>
+<li>Adding licensing to an application does not affect the way the application
+functions when run on a device that does not offer Android Market.</li>
+<li>Licensing is currently for paid apps only, since free apps are considered
+licensed for all users. If your application is already published as free,
+you won't be able to upload a new version that uses licensing.</li>
+</ul>
+
+<h4>Replacement for copy protection</h4>
+
+<p>Android Market Licensing is a flexible, secure mechanism for controlling
+access to your applications. It effectively replaces the copy-protection
+mechanism offered on Android Market and gives you wider distribution
+potential for your applications. </p>
+
+<ul>
+<li>A limitation of the legacy copy-protection mechanism on Android Market is
+that applications using it can be installed only on compatible devices that
+provide a secure internal storage environment. For example, a copy-protected
+application cannot be downloaded from Market to a device that provides root
+access, and the application cannot be installed to a device's SD card. </li>
+<li>With Android Market licensing, you can move to a license-based model in
+which access is not bound to the characteristics of the host device, but to your
+publisher account on Android Market and the licensing policy that you define.
+Your application can be installed and controlled on any compatible device on
+any storage, including SD card.</li>
+</ul>
+
+<p>Although no license mechanism can completely prevent all unauthorized use,
+the licensing service lets you control access for most types of normal usage,
+across all compatible devices, locked or unlocked, that run Android 1.5 or
+higher version of the platform.</p>
+
+<p>The sections below describe how to add Android Market licensing to your
+applications. </p>
+
+<h2 id="account">Setting Up a Publisher Account</h2>
+
+<p>Android Market licensing lets you manage access to applications that
+users have downloaded from Android Market. To use licensing in an application,
+you need to have a publisher account on Android Market so that you can
+publish the application to users. </p>
+
+<p>If you don't already have a publisher account, you need to register for one
+using your Google account and agree to the terms of service. Once you are
+registered, you can upload applications at your convenience and begin debugging
+and testing your licensing implementation. For more information about publishing
+on Android Market, see <a
+href="{@docRoot}guide/publishing/publishing.html">Publishing Your
+Applications</a></p>
+
+<p>To register as an Android Market developer and set up your publisher account,
+visit the Android Market publisher site:</p>
+
+<p style="margin-left:2em;"><a
+href="http://market.android.com/publish">http://market.android.com/publish</a>
+</p>
+
+<p>If you already have a publisher account on Android Market, use your existing
+account to set up licensing. You <em>do not</em> need to register for a new
+account to support licensing (and doing so is not recommended, especially if you
+are adding licensing support to applications that you have already published).
+In all cases, if you have published applications, you manage licensing for those
+applications through the account on which the applications are published. </p>
+
+<p>Once your publisher account is set up, use the account to:</p>
+
+<ul>
+<li>Obtain a public key for licensing</li>
+<li>Debug and test an application's licensing implementation, prior to
+publishing the application</li>
+<li>Publish the applications to which you have added licensing support</li>
+</ul>
+
+<h4>Administrative settings for licensing</h4>
+
+<p>Once you are signed into your publisher account, you can manage several
+administrative controls for Android Market licensing. The controls are available
+in the Edit Profile page, in the "Licensing" panel, shown below. The controls
+let you: </p>
+
+<ul>
+<li>Set up multiple "test accounts", identified by email address. The licensing
+server allows users signed into test accounts on a device or emulator to send
+license checks and receive static test responses.</li>
+<li>Obtain the account's public key for licensing. When you are implementing
+licensing in an application, you must copy the public key string into the
+application.</li>
+<li>Configure static test responses that the server sends, when it receives a
+license check for an application uploaded to the publisher account, from a user
+signed in to the publisher account or a test account.</li>
+</ul>
+
+<div style="margin-bottom:2em;">
+
+<img src="{@docRoot}images/licensing_public_key.png" style="text-align:left;margin-bottom:0;" />
+<div style="margin:0 2em;padding:0"><strong>Figure 2.</strong> The Licensing
+panel of your account's Edit Profile page lets you manage administrative
+settings for licensing.</div>
+</div>
+
+<p>For more information about how to work with test accounts and static test
+responses, see <a href="#test-env">Setting Up a Testing Environment</a>, below.
+
+<h2 id="dev-setup">Setting Up the Development Environment</h2>
+
+<p>Once you've set up your publisher account on Android Market, the next step is
+to set up your development environment for licensing. </p>
+
+<p>Setting up your environment for licensing involves these tasks:</p>
+
+<ol>
+<li><a href="#download-sdk">Downloading the latest SDK</a>, if you haven't already done so </li>
+<li><a href="#runtime-setup">Setting up the runtime environment</a> for development</a></li>
+<li><a href="#download-lvl">Downloading the Market Licensing component</a> into your SDK </li>
+<li><a href="#lvl-setup">Setting up the Licensing Verification Library</a></li>
+<li><a href="#add-library">Including the LVL library project in your application</a></li>
+</ol>
+
+<p>The sections below describe these tasks. When you are done with setup,
+you can begin <a href="#app-integration">integrating the LVL into your applications</a>.</p>
+
+<p>To get started, you need to set up a proper runtime environment on which
+you can run, debug and test your application's implementation of license
+checking and enforcement. </p>
+
+
+<h3 id="download-sdk">Downloading the latest SDK</h3>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Licensing sample application</h2>
+
+<p>To work with Android Market licensing, you need a functioning Android
+application to which you can add licensing support. </p>
+
+<p style="margin-top:.5em;">If you are new to Android
+and don't yet have a functioning application, the LVL component includes a sample
+application that you can set up as a new application project. The sample provides
+a complete, working example of how licensing works. For more information, see <a
+href="#download-lvl">Downloading the LVL</a>.</p>
+
+</div>
+</div>
+
+<p>If you haven't done so, you need to download the Android SDK before you can
+develop Android applications. The SDK provides the tools that you need to build
+and debug Android applications, including applications that use Android Market
+licensing. For complete information, including installation instructions, see
+the <a href="{@docRoot}sdk/index.html">Android SDK</a>. </p>
+
+<p>If you have already installed the SDK, make sure to update the
+SDK tools and ADT Plugin to the latest versions. You can update the SDK tools
+using the Android SDK and AVD Manager and ADT through <strong>Help</strong> &gt;
+<strong>Software Updates...</strong> in Eclipse. </p>
+
+<p>After you've installed the latest SDK and tools, set up your development
+environment as described below. </p>
+
+
+<h3 id="runtime-setup">Setting up the runtime environment</h3>
+
+<p>As described earlier, applications check licensing status not by contacting
+the licensing server directly, but by binding to a service provided by the
+Android Market application and initiating a license check request. The Android
+Market service then handles the direct communication with the licensing server
+and finally routes the response back to your application. To debug and test
+licensing in your application, you need to set up a runtime environment that
+includes the necessary Android Market service, so that your application is able
+to send license check requests to the licensing server. </p>
+
+<p>There are two types of runtime environment that you can use: </p>
+
+<ul>
+<li>An Android-powered device that includes the Android Market application, or</li>
+<li>An Android emulator running the Google APIs Add-on, API level 8 (release 2)
+or higher</li>
+</ul>
+
+<p>The sections below provide more information. </p>
+
+<h4 id="runtime-device">Running on a device</h4>
+
+<p>You can use an Android-powered device as the runtime environment for
+debugging and testing licensing on your application.</p>
+
+<p>The device you use must:</p>
+
+<ul>
+<li>Run a standard version of the Android 1.5 or later (API level
+3 or higher) platform, <em>and</em> </li>
+<li>Run a system image on which the Android Market client application
+is preinstalled. </li>
+</ul>
+
+<p>If Android Market is not preinstalled in the system image, your application won't
+be able to communicate with the Android Market licensing server. </p>
+
+<p>For general information about how to set up a device for use in developing
+Android applications, see <a
+href="{@docRoot}guide/developing/device.html">Developing on a Device</a>.</p>
+
+<h4 id="runtime-emulator">Running on an Android emulator</h4>
+
+<p>You can also use an Android emulator as your runtime
+environment for debugging and testing licensing.</p>
+
+<p>Because the standard Android platforms provided in the Android SDK <em>do
+not</em> include Android Market, you need to download the Google APIs Add-On
+platform, API Level 8 (or higher), from the SDK repository. After downloading
+the add-on, you need to create an AVD configuration that uses that system image.
+</p>
+
+<p>The Google APIs Add-On does not include the full Android Market client.
+However, it does provide: </p>
+
+<ul>
+<li>An Android Market background service that implements the
+ILicensingService remote interface, so that your application can
+send license checks over the network to the licensing server. </li>
+<li>A set of underlying account services that let you add an a Google account on
+the AVD and sign in using your publisher account or test account credentials.
+Signing in using your publisher or test account enables you to debug and test
+your application without having publish it. For more information see <a
+href="#acct-signin">Signing in to an authorized account</a>, below.</li>
+</ul>
+
+<p>Several versions of the add-on are available in the SDK repository, but only
+<strong>Google APIs Add-On, API 8 (release 2) or higher</strong> version of the
+add-on includes the necessary Android Market services. This means that you
+cannot use Google APIs Add-On API 7 or lower as a runtime environment for
+developing licensing on an emulator.</p>
+
+<div style="margin-bottom:2em;">
+
+<img src="{@docRoot}images/licensing_gapis_8.png" style="text-align:left;margin-bottom:0;" />
+<div style="margin:0 2em;padding:0"><strong>Figure 3.</strong> Google APIs
+Add-On, API 8 (release 2) or higher lets you debug and test your licensing
+implementation in an emulator.</div>
+</div>
+
+<p>To set up an emulator for adding licensing to an application, follow
+these steps: </p>
+
+<ol>
+ <li>Launch the Android SDK and AVD Manager. </li>
+ <li>In the <strong>Available Packages</strong> panel, select and download the
+SDK component "Google APIs (Google Inc.) - API Level 8" (or higher) from the SDK
+repository, as shown in the figure above.
+ <p>When the download is complete, use the Android SDK and AVD Manager to
+create a new AVD based on that component, described next.</p></li>
+ <li>In the <strong>Virtual
+Devices</strong> panel of the Android SDK and AVD Manager, click
+<strong>New</strong> and set the configuration details for the new AVD. </li>
+ <li>In the dialog that appears, assign a descriptive name to the AVD and then
+use the "Target" menu to choose the "Google APIs (Google Inc.) - API Level 8" as
+the system image to run on the new AVD. Set the other configuration details as
+needed and then click <strong>Create AVD</strong> to finish. The SDK tools
+create the new AVD configuration, which then appears in the list of available
+Android Virtual Devices.</li>
+</ol>
+
+<p>If you are not familiar with AVDs or how to use them, see <a
+href="{@docRoot}guide/developing/tools/avd.html">Android Virtual Devices</a>.</p>
+
+<h4 id="project-update">Updating your project configuration</h4>
+
+<p>After you set up a runtime environment that meets the requirements described
+above &mdash; either on an actual device or on an emulator &mdash; make sure to
+update your application project or build scripts as needed, so that your compiled
+<code>.apk</code> files that use licensing are deployed into that environment.
+In particular, if you are developing in Eclipse, make sure that you set up a
+Run/Debug Configuration that targets the appropriate device or AVD. </p>
+
+<p>You do not need to make any changes to your application's
+build configuration, provided that the project is already configured to compile
+against a standard Android 1.5 (API level 3) or higher library. For example:
+
+<ul>
+<li>If you have an existing application that is compiled against
+the Android 1.5 library, you do not need to make any changes to your
+build configuration to support licensing. The build target meets the minimum
+requirements for licensing, so you would continue building
+against the same version of the Android platform.</li>
+
+<li>Similarly, if you are building against Android 1.5 (API level 3) but
+are using an emulator running the Google APIs Add-On API 8 as the application's
+runtime environment, there is no need to change your application's build
+configuration. </li>
+</ul>
+
+<p>In general, adding licensing to an application should have no impact
+whatsoever on the application's build configuration.</p>
+
+
+<h3 id="download-lvl">Downloading the LVL</h3>
+
+<p>The License Verification Library (LVL) is a collection of helper classes that
+greatly simplify the work that you need to do to add licensing to your
+application. In all cases, we recommend that you download the LVL and use it as
+the basis for the licensing implementation in your application.</p>
+
+<p>The LVL is available as a downloadable component of the Android SDK. The
+component includes: </p>
+
+<ul>
+<li>The LVL sources, stored inside an Android library project. </li>
+<li>An example application called "sample" that depends on the LVL library
+project. The example illustrates how an application uses the library helper
+classes to check and enforce licensing.</li>
+</ul>
+
+<p>To download the LVL component into your development environment, use the
+Android SDK and AVD Manager. Launch the Android SDK and AVD Manager and then
+select the "Market Licensing" component, as shown in the figure below.
+Accept the terms and click <strong>Install Selected</strong> to begin the download. </p>
+
+<div style="margin-bottom:2em;">
+
+<img src="{@docRoot}images/licensing_package.png" style="text-align:left;margin-bottom:0;" />
+<div style="margin:0 2em;padding:0"><strong>Figure 4.</strong> The Market
+Licensing package contains the LVL and the LVL sample application. </div>
+</div>
+
+<p>When the download is complete, the Android SDK and AVD Manager installs both
+the LVL library project and the example application into these directories: </p>
+
+<p style="margin-left:2em"><code>&lt;<em>sdk</em>&gt;/market_licensing/library/</code>
+&nbsp;&nbsp;(the LVL library project)<br />
+<code>&lt;<em>sdk</em>&gt;/market_licensing/sample/</code>&nbsp;&nbsp;(the example
+application)</p>
+
+<p>If you aren't familiar with how to download components into your SDK, see the
+<a href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a>
+document. </p>
+
+
+<h3 id="lvl-setup">Setting Up the Licensing Verification Library</h3>
+
+<p>After downloading the LVL to your computer, you need to set it up in your
+development environment, either as an Android library project or by
+copying (or importing) the library sources directly into your existing
+application package. In general, using the LVL as a library project is recommended,
+since it lets you reuse your licensing code across multiple applications and
+maintain it more easily over time. Note that the LVL is not designed to be
+compiled separately and added to an application as a static .jar file. </p>
+
+<h4>Moving the library sources to a new location</h4>
+
+<p>Because you will be customizing the LVL sources to some extent, you should
+make sure to <em>move or copy</em> the library sources (the entire
+directory at <code>&lt;<em>sdk</em>&gt;/market_licensing/library/</code>)
+to a working directory outside of the SDK. You should then use the relocated
+sources as your working set. If you are using a source-code management
+system, add and track the sources that are in the working location rather
+than those in default location in the SDK. </p>
+
+<p>Moving the library sources is important is because, when you later update the
+Market licensing package, the SDK installs the new files to the same location as
+the older files. Moving your working library files to a safe location ensures
+that your work won't be inadvertently overwritten should you download a new
+version of the LVL.</p>
+
+<h4>Creating the LVL as a library project</h4>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Working with library projects</h2>
+
+<p>The LVL is provided as an Android library project, which means that you can
+share its code and resources across multiple applications. </p>
+
+<p style="margin-top:.5em;">If you aren't familiar with library projects or how
+to use them, read more in <a
+href="{@docRoot}guide/developing/eclipse-adt.html#libraryProject">Developing in
+Eclipse with ADT</a> or <a
+href="{@docRoot}guide/developing/other-ide.html#libraryProject">Developing in
+Other IDEs</a>, as appropriate for your environment.</p>
+
+</div>
+</div>
+
+<p>The recommended way of using the LVL is setting it up as a new Android
+<em>library project</em>. A library project is a type of development project
+that holds shared Android source code and resources. Other Android application
+projects can reference the library project and, at build time, include its
+compiled sources in their <code>.apk</code> files. In the context of licensing,
+this means that you can do most of your licensing development once, in a library
+project, then include the library sources in your various application projects.
+In this way, you can easily maintain a uniform implementation of licensing
+across all of your projects and maintain it centrally. </p>
+
+<p>The LVL is provided as a configured library project &mdash; once you have
+downloaded it, you can start using it right away. </p>
+
+<p>If you are working in Eclipse with ADT, you need to add the LVL to your
+workspace as a new development project, in the same way as you would a new
+application project. </p>
+
+<ol>
+<li>Use the New Project Wizard to create a new
+project from existing sources. Select the LVL's <code>library</code> directory
+(the directory containing the library's AndroidManifest.xml file) as the project
+root.</li>
+<li>When you are creating the library project, you can select any application
+name, package, and set other fields as needed. </li>
+<li>For the library's build target, select Android 1.5 (API level 3) or higher.</li>
+</ol>
+
+<p> When created, the project is
+predefined as a library project in its <code>default.properties</code> file, so
+no further configuration is needed. </p>
+
+<p>For more information about how to create an application project or work with
+library projects in Eclipse, see <a
+href="{@docRoot}guide/developing/eclipse-adt.html#CreatingAProject">Developing
+in Eclipse with ADT</a>.</p>
+
+<h4>Copying the LVL sources to your application</h4>
+
+<p>As an alternative to adding the LVL as a library project, you can copy the
+library sources directly into your application. To do so, copy (or import) the
+LVL's <code>library/src/com</code> directory into your application's
+<code>src/</code> directory.</p>
+
+<p>If you add the LVL sources directly to your application, you can skip the
+next section and start working with the library, as described in <a
+href="#app-integration"></a>.</p>
+
+
+<h3 id="add-library">Including the LVL library project sources in your
+application</h3>
+
+<p>If you want to use the LVL sources as a library project, you need to add a
+reference to the LVL library project in your application project properties. This tells
+build tools to include the LVL library project sources in your application at
+compile time. The process for adding a reference to a library project depends
+on your development environment, as described below.</p>
+
+<p> If you are developing in Eclipse with ADT, you should already have added the
+library project to your workspace, as described in the previous section. If you
+haven't done that already, do it now before continuing. </p>
+
+<p>Next, open the application's project properties window, as shown below.
+Select the "Android" properties group and click <strong>Add</strong>, then
+choose the LVL library project (com_android_vending_licensing) and click
+<strong>OK</strong>. For more information, see
+<a href="{@docRoot}guide/developing/eclipse-adt.html#libraryProject">Developing
+in Eclipse with ADT</a></p>
+
+<div style="margin-bottom:2em;">
+
+<img src="{@docRoot}images/licensing_add_library.png" style="text-align:left;margin-bottom:0;" />
+<div style="margin:0 2em;padding:0"><strong>Figure 5.</strong> If you are
+working in Eclipse with ADT, you can add the LVL library project to your
+application from the application's project properties.</div>
+</div>
+
+<p>If you are developing using the SDK command-line tools, navigate to the
+directory containing your application project and open the
+<code>default.properties</code> file. Add a line to the file that specifies the
+<code>android.library.reference.&lt;n&gt;</code> key and the path to the
+library. For example: </p>
+
+<pre>android.library.reference.1=path/to/library_project</pre>
+
+<p>Alternatively, you can use this command to update the project
+properties, including the reference to the library project:</p>
+
+<pre class="no-pretty-print" style="color:black">android update lib-project
+--target <em>&lt;target_ID&gt;</em> \
+--path <em>path/to/my/app_project</em> \
+--library <em>path/to/my/library_project</em>
+</pre>
+
+<p>For more information about working with library projects, see <a
+href="{@docRoot}guide/developing/eclipse-adt.html#libraryProject">Developing
+in Eclipse with ADT</a> or <a
+href="{@docRoot}guide/developing/other-ide.html#libraryProject">Developing in
+Other IDEs</a>, as appropriate for your environment.</p>
+
+
+<h2 id="app-integration">Integrating the LVL with Your Application</h2>
+
+<p>Once you've followed the steps above to set up a publisher account and
+development environment, you are ready to begin integrating the LVL with your
+application. </p>
+
+<p>Integrating the LVL with your application code involves these tasks:</p>
+
+<ol>
+<li><a href="#manifest-permission">Adding the licensing permission</a> your application's manifest.</li>
+<li><a href="#impl-Policy">Implementing a Policy</a> &mdash; you can choose one of the full implementations provided in the LVL or create your own.</li>
+<li><a href="#impl-Obfuscator">Implementing an Obfuscator</a>, if your Policy will cache any license response data. </li>
+<li><a href="#impl-lc">Adding code to check the license</a> in your application's main Activity</li>
+<li><a href="#impl-DeviceLimiter">Implementing a DeviceLimiter</a> (optional and not recommended for most applications)</li>
+</ol>
+
+<p>The sections below describe these tasks. When you are done with the
+integration, you should be able to compile your application successfully and you
+can begin testing, as described in <a href="#test-env">Setting Up the Test
+Environment</a>.</p>
+
+<p>For an overview of the full set of source files included in the LVL, see <a
+href="#lvl-summary">Summary of LVL Classes and Interfaces</a>.</p>
+
+
+<h3 id="manifest-permission">Adding the licensing permission to your
+AndroidManifest.xml</h3>
+
+<p>To use the Android Market application for sending a license check to the
+server, your application must request the proper permission,
+<code>com.android.vending.CHECK_LICENSE</code>. If your application does
+not declare the licensing permission but attempts to initiate a license check,
+the LVL throws a security exception.</p>
+
+<p>To request the licensing permission in your application, declare a <a
+href="{@docRoot}guide/topics/manifest/uses-permission-element.html"><code>&lt;uses-permission&gt;</code></a>
+element as a child of <code>&lt;manifest&gt;</code>, as follows: </p>
+
+<p style="margin-left:2em;"><code>&lt;uses-permission
+android:name="com.android.vending.CHECK_LICENSE"&gt;</code></p>
+
+<p>For example, here's how the LVL sample application declares the permission:
+</p>
+
+<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt;
+
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android" ..."&gt;
+ ...
+ &lt;!-- Devices &gt;= 3 have version of Android Market that supports licensing. --&gt;
+ &lt;uses-sdk android:minSdkVersion="3" /&gt;
+ &lt;!-- Required permission to check licensing. --&gt;
+ &lt;uses-permission android:name="com.android.vending.CHECK_LICENSE" /&gt;
+ ...
+&lt;/manifest&gt;
+</pre>
+
+<p class="note"><strong>Note:</strong> Currently, you cannot declare the
+<code>CHECK_LICENSE</code> permission in the LVL library project's manifest,
+because the SDK Tools will not merge it into the manifests of dependent
+applications. Instead, you must declare the permission in each dependent
+application's manifest. </p>
+
+
+<h3 id="impl-Policy">Implementing a Policy</h3>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>ServerManagedPolicy</h2>
+
+<p>The LVL includes a complete Policy implementation called ServerManagedPolicy
+that makes use of license-management settings provided by the Android Market
+server. </p>
+
+<p style="margin-top:.5em;">Use of ServerManagedPolicy as the basis for your
+Policy is strongly recommended. For more information, see <a
+href="#ServerManagedPolicy">ServerManagedPolicy</a> section, below.</p>
+
+</div>
+</div>
+
+<p>Android Market licensing service does not itself determine whether a
+given user with a given license should be granted access to your application.
+Rather, that responsibility is left to a Policy implementation that you provide
+in your application.</p>
+
+<p>Policy is an interface declared by the LVL that is designed to hold your
+application's logic for allowing or disallowing user access, based on the result
+of a license check. To use the LVL, your application <em>must</em> provide an
+implementation of Policy. </p>
+
+<p>The Policy interface declares two methods, <code>allowAccess()</code> and
+<code>processServerResponse()</code>, which are called by a LicenseChecker
+instance when processing a response from the license server. It also declares an
+enum called <code>LicenseResponse</code>, which specifies the license response
+value passed in calls to <code>processServerResponse()</code>. </p>
+
+<ul>
+<li><code>processServerResponse()</code> lets you preprocess the raw response
+data received from the licensing server, prior to determining whether to grant
+access.
+
+<p>A typical implementation would extract some or all fields from the license
+response and store the data locally to a persistent store, such as through
+{@link android.content.SharedPreferences} storage, to ensure that the data is
+accessible across application invocations and device power cycles. For example,
+a Policy would maintain the timestamp of last successful license check, the
+retry count, the license validity period, and similar information in a
+persistent store, rather than resetting the values each time the application is
+launched.</p>
+
+<p>When storing response data locally, the Policy must ensure that the data is
+obfuscated (see <a href="#impl-Obfuscator">Implementing an Obfuscator</a>,
+below).</p></li>
+
+<li><code>allowAccess()</code> determines whether to grant the user access to
+your application, based on any available license response data (from the
+licensing server or from cache) or other application-specific information. For
+example, your implementation of <code>allowAccess()</code> could take into
+account additional criteria, such as usage or other data retrieved from a
+backend server. In all cases, an implementation of <code>allowAccess()</code>
+should only return <code>true</code> if the user is licensed to use the
+application, as determined by the licensing server, or if there is a transient
+network or system problem that prevents the license check from completing. In
+such cases, your implementation can maintain a count of retry responses and
+provisionally allow access until the next license check is complete.</li>
+
+</ul>
+
+<p>To simplify the process of adding licensing to your application and to
+provide an illustration of how a Policy should be designed, the LVL includes
+two full Policy implementations that you can use without modification or
+adapt to your needs:</p>
+
+<ul>
+<li><a href="#ServerManagedPolicy">ServerManagedPolicy</a>, a flexible Policy
+that uses server-provided settings and cached responses to manage access across
+varied network conditions, and</li>
+<li><a href="#StrictPolicy">StrictPolicy</a>, which does not cache any response
+data and allows access <em>only</em> if the server returns a licensed
+response.</li>
+</ul>
+
+<p>For most applications, the use of ServerManagedPolicy is highly
+recommended. ServerManagedPolicy is the LVL default and is integrated with
+the LVL sample application.</p>
+
+
+<h4 id="custom-policies">Guidelines for custom policies</h4>
+
+<p>In your licensing implementation, you can use one of the complete policies
+provided in the LVL (ServerManagedPolicy or StrictPolicy) or you can create a
+custom policy. For any type of custom policy, there are several important design
+points to understand and account for in your implementation.</p>
+
+<p>The licensing server applies general request limits to guard against overuse
+of resources that could result in denial of service. When an application exceeds
+the request limit, the licensing server returns a 503 response, which gets
+passed through to your application as a general server error. This means that no
+license response will be available to the user until the limit is reset, which
+can affect the user for an indefinite period.</p>
+
+<p>If you are designing a custom policy, we recommend that the Policy:
+<ol>
+<!-- <li>Limits the number of points at which your app calls for a license check
+to the minimum. </li> -->
+<li>Caches (and properly obfuscates) the most recent successful license response
+in local persistent storage.</li>
+<li>Returns the cached response for all license checks, for as long as the
+cached response is valid, rather than making a request to the licensing server.
+Setting the response validity according to the server-provided <code>VT</code>
+extra is highly recommended. See <a href="#extras">Server Response Extras</a>
+for more information.</li>
+<li>Uses an exponential backoff period, if retrying any requests the result in
+errors. Note that the Android Market client automatically retries failed
+requests, so in most cases there is no need for your Policy to retry them.</li>
+<li>Provides for a "grace period" that allows the user to access your
+application for a limited time or number of uses, while a license check is being
+retried. The grace period benefits the user by allowing access until the next
+license check can be completed successfully and it benefits you by placing a
+hard limit on access to your application when there is no valid license response
+available.</li>
+</ol>
+
+<p>Designing your Policy according to the guidelines listed above is critical,
+because it ensures the best possible experience for users while giving you
+effective control over your application even in error conditions. </p>
+
+<p>Note that any Policy can use settings provided by the licensing server to
+help manage validity and caching, retry grace period, and more. Extracting the
+server-provided settings is straightforward and making use of them is highly
+recommended. See the ServerManagedPolicy implementation for an example of how to
+extract and use the extras. For a list of server settings and information about
+how to use them, see <a href="#extras">Server Response Extras</a> in the
+Appendix of this document.</p>
+
+<h4 id="ServerManagedPolicy">ServerManagedPolicy</h4>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Server Response Extras</h2>
+
+<p>For certain types of licensing responses, the licensing server appends extra
+settings to the responses, to help the application manage licensing effectively.
+</p>
+
+<p style="margin-top:.5em;">See <a href="#extras">Server Response Extras</a> for
+a list of settings and <code>ServerManagedPolicy.java</code> for information
+about how a Policy can use the extras.</p>
+
+</div>
+</div>
+
+<p>The LVL includes a full and recommended implementation of the Policy
+interface called ServerManagedPolicy. The implementation is integrated with the
+LVL classes and serves as the default Policy in the library. </p>
+
+<p>ServerManagedPolicy provides all of the handling for license and retry
+responses. It caches all of the response data locally in a
+{@link android.content.SharedPreferences} file, obfuscating it with the
+application's Obfuscator implementation. This ensures that the license response
+data is secure and persists across device power cycles. ServerManagedPolicy
+provides concrete implementations of the interface methods
+<code>processServerResponse()</code> and <code>allowAccess()</code> and also
+includes a set of supporting methods and types for managing license
+responses.</p>
+
+<p>Importantly, a key feature of ServerMangedPolicy is its use of
+server-provided settings as the basis for managing licensing across an
+application's refund period and through varying network and error conditions.
+When an application contacts the Android Market server for a license check, the
+server appends several settings as key-value pairs in the extras field of certain
+license response types. For example, the server provides recommended values for the
+application's license validity period, retry grace period, and maximum allowable
+retry count, among others. ServerManagedPolicy extracts the values from the
+license response in its <code>processServerResponse()</code> method and checks
+them in its <code>allowAccess()</code> method. For a list of the server-provided
+settings used by ServerManagedPolicy, see <a href="#extras">Server Response
+Extras</a> in the Appendix of this document.</p>
+
+<p>For convenience, best performance, and the benefit of using license settings
+from the Android Market server, <strong>using ServerManagedPolicy as your
+licensing Policy is strongly recommended</strong>. </p>
+
+<p>If you are concerned about the security of license response data that is
+stored locally in SharedPreferences, you can use a stronger obfuscation
+algorithm or design a stricter Policy that does not store license data. The LVL
+includes an example of such a Policy &mdash; see <a
+href="#StrictPolicy">StrictPolicy</a> for more information.</p>
+
+<p>To use ServerManagedPolicy, simply import it to your Activity, create an
+instance, and pass a reference to the instance when constructing your
+LicenseChecker. See <a href="#lc-lcc">Instantiate LicenseChecker and
+LicenseCheckerCallback</a> for more information. </p>
+
+<h4 id="StrictPolicy">StrictPolicy</h4>
+
+<p>The LVL includes an alternative full implementation of the Policy interface
+called StrictPolicy. The StrictPolicy implementation provides a more restrictive
+Policy than ServerManagedPolicy, in that it does not allow the user to access
+the application unless a license response is received from the server at the
+time of access that indicates that the user is licensed.</p>
+
+<p>The principal feature of StrictPolicy is that it does not store <em>any</em>
+license response data locally, in a persistent store. Because no data is stored,
+retry requests are not tracked and cached responses can not be used to fulfill
+license checks. The Policy allows access only if:</p>
+
+<ul>
+<li>The license response is received from the licensing server, and </li>
+<li>The license response indicates that the user is licensed to access the
+application. </li>
+</ul>
+
+<p>Using StrictPolicy is appropriate if your primary concern is to ensure that,
+in all possible cases, no user will be allowed to access the application unless
+the user is confirmed to be licensed at the time of use. Additionally, the
+Policy offers slightly more security than ServerManagedPolicy &mdash; since
+there is no data cached locally, there is no way a malicious user could tamper
+with the cached data and obtain access to the application.</p>
+
+<p>At the same time, this Policy presents a challenge for normal users, since it
+means that they won't be able to access the application when there is no network
+(cell or wi-fi) connection available. Another side-effect is that your
+application will send more license check requests to the server, since using a
+cached response is not possible.</p>
+
+<p>Overall, this policy represents a tradeoff of some degree of user convenience
+for absolute security and control over access. Consider the tradeoff carefully
+before using this Policy.</p>
+
+<p>To use StrictPolicy, simply import it to your Activity, create an instance,
+and pass a reference to it when constructing your LicenseChecker. See
+<a href="#lc-lcc">Instantiate LicenseChecker and LicenseCheckerCallback</a>
+for more information. </p>
+
+<h3 id="impl-Obfuscator">Implementing an Obfuscator</h3>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>AESObfuscator</h2>
+
+<p>The LVL includes a full Obfuscator implementation in the
+<code>AESObfuscator.java</code> file. The Obfuscator uses AES encryption to
+obfuscate/unobfuscate data. If you are using a Policy (such as
+ServerManagedPolicy) that caches license response data, using AESObfuscator as
+basis for your Obfuscator implementation is highly recommended. </p>
+
+</div>
+</div>
+
+<p>A typical Policy implementation needs to save the license response data for
+an application to a persistent store, so that it is accessible across
+application invocations and device power cycles. For example, a Policy would
+maintain the timestamp of the last successful license check, the retry count,
+the license validity period, and similar information in a persistent store,
+rather than resetting the values each time the application is launched. The
+default Policy included in the LVL, ServerManagedPolicy, stores license response
+data in a {@link android.content.SharedPreferences} instance, to ensure that the
+data is persistent. </p>
+
+<p>Because the Policy will use stored license response data to determine whether
+to allow or disallow access to the application, it <em>must</em> ensure that any
+stored data is secure and cannot be reused or manipulated by a root user on a
+device. Specifically, the Policy must always obfuscate the data before storing
+it, using a key that is unique for the application and device. Obfuscating using
+a key that is both application-specific and device-specific is critical, because
+it prevents the obfuscated data from being shared among applications and
+devices.</p>
+
+<p>The LVL assists the application with storing its license response data in a
+secure, persistent manner. First, it provides an Obfuscator
+interface that lets your application supply the obfuscation algorithm of its
+choice for stored data. Building on that, the LVL provides the helper class
+PreferenceObfuscator, which handles most of the work of calling the
+application's Obfuscator class and reading and writing the obfuscated data in a
+SharedPreferences instance. </p>
+
+<p>The LVL provides a full Obfuscator implementation called
+AESObfuscator that uses AES encryption to obfuscate data. You can
+use AESObfuscator in your application without modification or you
+can adapt it to your needs. For more information, see the next section.</p>
+
+
+<h4 id="AESObfuscator">AESObfuscator</h4>
+
+<p>The LVL includes a full and recommended implementation of the Obfuscator
+interface called AESObfuscator. The implementation is integrated with the
+LVL sample application and serves as the default Obfuscator in the library. </p>
+
+<p>AESObfuscator provides secure obfuscation of data by using AES to
+encrypt and decrypt the data as it is written to or read from storage.
+The Obfuscator seeds the encryption using three data fields provided
+by the application: </p>
+
+<ol>
+<li>A salt &mdash; an array of random bytes to use for each (un)obfuscation. </li>
+<li>An application identifier string, typically the package name of the application.</li>
+<li>A device identifier string, derived from as many device-specific sources
+as possible, so as to make it as unique.</li>
+</ol>
+
+<p>To use AESObfuscator, first import it to your Activity. Declare a private
+static final array to hold the salt bytes and initialize it to 20 randomly
+generated bytes.</p>
+
+<pre> ...
+ // Generate 20 random bytes, and put them here.
+ private static final byte[] SALT = new byte[] {
+ -46, 65, 30, -128, -103, -57, 74, -64, 51, 88, -95,
+ -45, 77, -117, -36, -113, -11, 32, -64, 89
+ };
+ ...
+</pre>
+
+<p>Next, declare a variable to hold a device identifier and generate a value for
+it in any way needed. For example, the sample application included in the LVL
+queries the system settings for the
+<code>android.Settings.Secure.ANDROID_ID</code>, which is unique to each device.
+</p>
+
+<p>Note that, depending on the APIs you use, your application might need to
+request additional permissions in order to acquire device-specific information.
+For example, to query the {@link android.telephony.TelephonyManager} to obtain
+the device IMEI or related data, the application will also need to request the
+<code>android.permission.READ_PHONE_STATE</code> permission in its manifest.</p>
+
+<p>Before requesting new permissions for the <em>sole purpose</em> of acquiring
+device-specific information for use in your Obfuscator, consider
+how doing so might affect your application or its filtering on Android Market
+(since some permissions can cause the SDK build tools to add
+the associated <code>&lt;uses-feature&gt;</code>).</p>
+
+<p>Finally, construct an instance of AESObfuscator, passing the salt,
+application identifier, and device identifier. You can construct the instance
+directly, while constructing your Policy and LicenseChecker. For example:</p>
+
+<pre> ...
+ // Construct the LicenseChecker with a Policy.
+ mChecker = new LicenseChecker(
+ this, new ServerManagedPolicy(this,
+ new AESObfuscator(SALT, getPackageName(), deviceId)),
+ BASE64_PUBLIC_KEY // Your public licensing key.
+ );
+ ...
+</pre>
+
+<p>For a complete example, see MainActivity in the LVL sample application.</p>
+
+
+<h3 id="impl-lc">Checking the license from your application's main Activity</h3>
+
+<p>Once you've implemented a Policy for managing access to your application, the
+next step is to add a license check to your application, which initiates a query
+to the licensing server if needed and manages access to the application based on
+the license response. All of the work of adding the license check and handling
+the response takes place in your main {@link android.app.Activity} source file.
+</p>
+
+<p>To add the license check and handle the response, you must:</p>
+
+<ol>
+ <li><a href="#imports">Add imports</a></li>
+ <li><a href="#lc-impl">Implement LicenseCheckerCallback</a> as a private inner class</li>
+ <li><a href="#thread-handler">Create a Handler</a> for posting from LicenseCheckerCallback to the UI thread</li>
+ <li><a href="#lc-lcc">Instantiate LicenseChecker</a> and LicenseCheckerCallback</li>
+ <li><a href="#check-access">Call checkAccess()</a> to initiate the license check</li>
+ <li><a href="#account-key">Embed your public key</a> for licensing</li>
+ <li><a href="#handler-cleanup">Call your LicenseChecker's onDestroy() method</a> to close IPC connections.</li>
+</ol>
+
+<p>The sections below describe these tasks. </p>
+
+<h4 id="lc-overview">Overview of license check and response</h4>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Example: MainActivity</h2>
+
+<p>The sample application included with the LVL provides a full example of how
+to initiate a license check and handle the result, in the
+<code>MainActivity.java</code> file.</p>
+
+</div>
+</div>
+
+<p>In most cases, you should add the license check to your application's main
+{@link android.app.Activity}, in the <code>onCreate()</code> method. This
+ensures that when the user launches your application directly, the license check
+will be invoked immediately. In some cases, you can add license checks in other
+locations as well. For example, if your application includes multiple Activity
+components that other applications can start by {@link android.content.Intent},
+you could add license checks in those Activities.</p>
+
+<p>A license check consists of two main actions: </p>
+
+<ul>
+<li>A call to a method to initiate the license check &mdash; in the LVL, this is
+a call to the <code>checkAccess()</code> method of a LicenseChecker object that
+you construct.</li>
+<li>A callback that returns the result of the license check. In the LVL, this is
+a <code>LicenseCheckerCallback</code> interface that you implement. The
+interface declares two methods, <code>allow()</code> and
+<code>dontAllow()</code>, which are invoked by the library based on to the
+result of the license check. You implement those two methods with whatever logic
+you need, to allow or disallow the user access to your application. Note that
+these methods do not determine <em>whether</em> to allow access &mdash; that
+determination is the responsibility of your Policy implementation. Rather, these
+methods simply provide the application behaviors for <em>how</em> to allow and
+disallow access (and handle application errors).</li>
+</ul>
+
+<div style="margin-bottom:2em;">
+
+<img src="{@docRoot}images/licensing_flow.png" style="text-align:left;margin-bottom:0;margin-left:3em;" />
+<div style="margin:.5em 0 1.5em 2em;padding:0"><strong>Figure 6.</strong> Overview of a
+typical license check interaction.</div>
+</div>
+
+<p>The diagram above illustrates how a typical license check takes place: </p>
+
+<ol>
+<li>Code in the application's main Activity instantiates LicenseCheckerCallback
+and LicenseChecker objects. When constructing LicenseChecker, the code passes in
+{@link android.content.Context}, a Policy implementation to use, and the
+publisher account's public key for licensing as parameters. </li>
+<li>The code then calls the <code>checkAccess()</code> method on the
+LicenseChecker object. The method implementation calls the Policy to determine
+whether there is a valid license response cached locally, in
+{@link android.content.SharedPreferences}.
+<ul>
+<li>If so, the <code>checkAccess()</code> implementation calls
+<code>allow()</code>.</li>
+<li>Otherwise, the LicenseChecker initiates a license check request that is sent
+to the licensing server.</li>
+</ul>
+</li>
+<li>When a response is received, LicenseChecker creates a LicenseValidator that
+verifies the signed license data and extracts the fields of the response, then
+passes them to your Policy for further evaluation.
+ <ul>
+ <li>If the license is valid, the Policy caches the response in
+SharedPreferences and notifies the validator, which then calls the
+<code>allow()</code> method on the LicenseCheckerCallback object. </li>
+ <li>If the license not valid, the Policy notifies the validator, which calls
+the <code>dontAllow()</code> method on LicenseCheckerCallback. </li>
+ </ul>
+</li>
+<li>In case of a recoverable local or server error, such as when the network is
+not available to send the request, LicenseChecker passes a RETRY response to
+your Policy's <code>processServerResponse()</code> method. </li>
+<li>In case of a application error, such as when the application attempts to
+check the license of an invalid package name, LicenseChecker passes an error
+response to the LicenseCheckerCallback's <code>applicationError()</code>
+method. </li>
+</ol>
+
+<p>Note that, in addition to initiating the license check and handling the
+result, which are described in the sections below, your application also needs
+to provide a <a href="#impl-Policy">Policy implementation</a> and, if the Policy
+stores response data (such as ServerManagedPolicy), an <a
+href="#impl-Obfuscator">Obfuscator</a> implementation. </p>
+
+
+<h4 id="imports">Add imports</h4>
+
+<p>First, open the class file of the application's main Activity and import
+LicenseChecker and LicenseCheckerCallback from the LVL package.</p>
+
+<pre> import com.android.vending.licensing.LicenseChecker;
+ import com.android.vending.licensing.LicenseCheckerCallback;</pre>
+
+<p>If you are using the default Policy implementation provided with the LVL,
+ServerManagedPolicy, import it also, together with the AESObfuscator. If you are
+using a custom Policy or Obfuscator, import those instead. </p>
+
+<pre> import com.android.vending.licensing.ServerManagedPolicy;
+ import com.android.vending.licensing.AESObfuscator;</pre>
+
+<h4 id="lc-impl">Implement LicenseCheckerCallback as a private inner class</h4>
+
+<p>LicenseCheckerCallback is an interface provided by the LVL for handling
+result of a license check. To support licensing using the LVL, you must
+implement LicenseCheckerCallback and
+its methods to allow or disallow access to the application.</p>
+
+<p>The result of a license check is always a call to one of the
+LicenseCheckerCallback methods, made based on the validation of the response
+payload, the server response code itself, and any additional processing provided
+by your Policy. Your application can implement the methods in any way needed. In
+general, it's best to keep the methods simple, limiting them to managing UI
+state and application access. If you want to add further processing of license
+responses, such as by contacting a backend server or applying custom constraints,
+you should consider incorporating that code into your Policy, rather than
+putting it in the LicenseCheckerCallback methods. </p>
+
+<p>In most cases, you should declare your implementation of
+LicenseCheckerCallback as a private class inside your application's main
+Activity class. </p>
+
+<p>Implement the <code>allow()</code> and <code>dontAllow()</code> methods as
+needed. To start with, you can use simple result-handling behaviors in the
+methods, such as displaying the license result in a dialog. This helps you get
+your application running sooner and can assist with debugging. Later, after you
+have determined the exact behaviors you want, you can add more complex handling.
+</p>
+
+<p>Some suggestions for handling unlicensed responses in
+<code>dontAllow()</code> include: </p>
+
+<ul>
+<li>Display a "Try again" dialog to the user, including a button to initiate a
+new license check. </li>
+<li>Display a "Purchase this application" dialog, including a button that
+deep-links the user to the application's details page on Market, from which the
+use can purchase the application. For more information on how to set up such
+links, see <a
+href="{@docRoot}guide/publishing/publishing.html#marketintent">Using Intents to
+Launch the Market Application on a Device</a>. </li>
+<li>Display a Toast notification that indicates that the features of the
+application are limited because it is not licensed. </li>
+</ul>
+
+<p>The example below shows how the LVL sample application implements
+LicenseCheckerCallback, with methods that display the license check result in a
+dialog. </p>
+
+<pre> private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
+ public void allow() {
+ if (isFinishing()) {
+ // Don't update UI if Activity is finishing.
+ return;
+ }
+ // Should allow user access.
+ displayResult(getString(R.string.allow));
+ }
+
+ public void dontAllow() {
+ if (isFinishing()) {
+ // Don't update UI if Activity is finishing.
+ return;
+ }
+ displayResult(getString(R.string.dont_allow));
+ // Should not allow access. An app can handle as needed,
+ // typically by informing the user that the app is not licensed
+ // and then shutting down the app or limiting the user to a
+ // restricted set of features.
+ // In this example, we show a dialog that takes the user to Market.
+ showDialog(0);
+ }
+ }
+</pre>
+
+<p>Additionally, you should implement the <code>applicationError()</code>
+method, which the LVL calls to let your application handle errors that are not
+retryable. For a list of such errors, see <a
+href="#server-response-codes">Server Response Codes</a> in the Appendix of this
+document. You can implement the method in any way needed. In most cases, the
+method should log the error code and call <code>dontAllow()</code>.</p>
+
+<h4 id="thread-handler">Create a Handler for posting from LicenseCheckerCallback
+to the UI thread</h4>
+
+<p>During a license check, the LVL passes the request to the Android Market
+application, which handles communication with the licensing server. The LVL
+passes the request over asynchronous IPC (using {@link android.os.Binder}) so
+the actual processing and network communication do not take place on a thread
+managed by your application. Similarly, when the Android Market application
+receives the result, it invokes a callback method over IPC, which in turn
+executes in an IPC thread pool in your application's process.</p>
+
+<p>The LicenseChecker class manages your application's IPC communication with
+the Android Market application, including the call that sends the request and
+the callback that receives the response. LicenseChecker also tracks open license
+requests and manages their timeouts. </p>
+
+<p>So that it can handle timeouts properly and also process incoming responses
+without affecting your application's UI thread, LicenseChecker spawns a
+background thread at instantiation. In the thread it does all processing of
+license check results, whether the result is a response received from the server
+or a timeout error. At the conclusion of processing, the LVL calls your
+LicenseCheckerCallback methods from the background thread. </p>
+
+<p>To your application, this means that:</p>
+
+<ol>
+<li>Your LicenseCheckerCallback methods will be invoked, in many cases, from a
+background thread.</li>
+<li>Those methods won't be able to update state or invoke any processing in the
+UI thread, unless you create a Handler in the UI thread and have your callback
+methods post to the Handler.</li>
+</ol>
+
+<p>If you want your LicenseCheckerCallback methods to update the UI thread,
+instantiate a {@link android.os.Handler} in the main Activity's
+{@link android.app.Activity#onCreate(android.os.Bundle) onCreate()} method,
+as shown below. In this example, the LVL sample application's
+LicenseCheckerCallback methods (see above) call <code>displayResult()</code> to
+update the UI thread through the Handler's
+{@link android.os.Handler#post(java.lang.Runnable) post()} method.</p>
+
+<pre>private Handler mHandler;
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ ...
+ mHandler = new Handler();
+ }
+</pre>
+
+<p>Then, in your LicenseCheckerCallback methods, you can use Handler methods to
+post Runnable or Message objects to the Handler. Here's how the sample
+application included in the LVL posts a Runnable to a Handler in the UI thread
+to display the license status.</p>
+
+<pre> private void displayResult(final String result) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ mStatusText.setText(result);
+ setProgressBarIndeterminateVisibility(false);
+ mCheckLicenseButton.setEnabled(true);
+ }
+ });
+ }
+</pre>
+
+<h4 id="lc-lcc">Instantiate LicenseChecker and LicenseCheckerCallback</h4>
+
+<p>In the main Activity's
+{@link android.app.Activity#onCreate(android.os.Bundle) onCreate()} method,
+create private instances of LicenseCheckerCallback and LicenseChecker. You must
+instantiate LicenseCheckerCallback first, because you need to pass a reference
+to that instance when you call the contructor for LicenseChecker. </p>
+
+<p>When you instantiate LicenseChecker, you need to pass in these parameters:</p>
+
+<ul>
+<li>The application {@link android.content.Context}</li>
+<li>A reference to the Policy implementation to use for the license check. In
+most cases, you would use the default Policy implementation provided by the LVL,
+ServerManagedPolicy. </li>
+<li>The String variable holding your publisher account's public key for
+licensing. </li>
+</ul>
+
+<p>If you are using ServerManagedPolicy, you won't need to access the class
+directly, so you can instantiate it in the LicenseChecker constructor,
+as shown in the example below. Note that you need to pass a reference to a new
+Obfuscator instance when you construct ServerManagedPolicy.</p>
+
+<p>The example below shows the instantiation of LicenseChecker and
+LicenseCheckerCallback from the <code>onCreate()</code> method of an Activity
+class. </p>
+
+<pre>public class MainActivity extends Activity {
+ ...
+ private LicenseCheckerCallback mLicenseCheckerCallback;
+ private LicenseChecker mChecker;
+
+ &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ...
+ // Construct the LicenseCheckerCallback. The library calls this when done.
+ mLicenseCheckerCallback = new MyLicenseCheckerCallback();
+
+ // Construct the LicenseChecker with a Policy.
+ mChecker = new LicenseChecker(
+ this, new ServerManagedPolicy(this,
+ new AESObfuscator(SALT, getPackageName(), deviceId)),
+ BASE64_PUBLIC_KEY // Your public licensing key.
+ );
+ ...
+ }
+}
+</pre>
+
+
+<p>Note that LicenseChecker calls the LicenseCheckerCallback methods from the UI
+thread <em>only</em> if there is valid license response cached locally. If the
+license check is sent to the server, the callbacks always originate from the
+background thread, even for network errors. </p>
+
+
+<h4 id="check-access">Call checkAccess() to initiate the license check</h4>
+
+<p>In your main Activity, add a call to the <code>checkAccess()</code> method of the
+LicenseChecker instance. In the call, pass a reference to your
+LicenseCheckerCallback instance as a parameter. If you need to handle any
+special UI effects or state management before the call, you might find it useful
+to call <code>checkAccess()</code> from a wrapper method. For example, the LVL
+sample application calls <code>checkAccess()</code> from a
+<code>doCheck()</code> wrapper method:</p>
+
+<pre> &#64;Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ...
+ // Call a wrapper method that initiates the license check
+ doCheck();
+ ...
+ }
+ ...
+ private void doCheck() {
+ mCheckLicenseButton.setEnabled(false);
+ setProgressBarIndeterminateVisibility(true);
+ mStatusText.setText(R.string.checking_license);
+ mChecker.checkAccess(mLicenseCheckerCallback);
+ }
+</pre>
+
+
+<h4 id="account-key">Embed your public key for licensing</h4>
+
+<p>For each publisher account, the Android Market service automatically
+generates a 2048-bit RSA public/private key pair that is used exclusively for
+licensing. The key pair is uniquely associated with the publisher account and is
+shared across all applications that are published through the account. Although
+associated with a publisher account, the key pair is <em>not</em> the same as
+the key that you use to sign your applications (or derived from it).</p>
+
+<p>The Android Market publisher site exposes the public key for licensing to any
+developer signed in to the publisher account, but it keeps the private key
+hidden from all users in a secure location. When an application requests a
+license check for an application published in your account, the licensing server
+signs the license response using the private key of your account's key pair.
+When the LVL receives the response, it uses the public key provided by the
+application to verify the signature of the license response. </p>
+
+<p>To add licensing to an application, you must obtain your publisher account's
+public key for licensing and copy it into your application. Here's how to find
+your account's public key for licensing:</p>
+
+<ol>
+<li>Go to the Android Market <a
+href="http://market.android.com/publish">publisher site</a> and sign in.
+Make sure that you sign in to the account from which the application you are
+licensing is published (or will be published). </li>
+<li>In the account home page, locate the "Edit profile" link and click it. </li>
+<li>In the Edit Profile page, locate the "Licensing" pane, shown below. Your
+public key for licensing is given in the "Public key" text box. </p>
+</ol>
+
+<p>To add the public key to your application, simply copy/paste the key string
+from the text box into your application as the value of the String variable
+<code>BASE64_PUBLIC_KEY</code>. When you are copying, make sure that you have
+selected the entire key string, without omitting any characters. </p>
+
+<p>Here's an example from the LVL sample application:</p>
+
+<pre> public class MainActivity extends Activity {
+ private static final String BASE64_PUBLIC_KEY = "MIIBIjANBgkqhkiG ... "; //truncated for this example
+ ...
+ }
+</pre>
+
+<h4 id="handler-cleanup">Call your LicenseChecker's onDestroy() method
+to close IPC connections</h4>
+
+<p>Finally, to let the LVL clean up before your application
+{@link android.content.Context} changes, add a call to the LicenseChecker's
+<code>onDestroy()</code> method from your Activity's
+{@link android.app.Activity#onDestroy()} implementation. The call causes the
+LicenseChecker to properly close any open IPC connection to the Android Market
+application's ILicensingService and removes any local references to the service
+and handler.</p>
+
+<p>Failing to call the LicenseChecker's <code>onDestroy()</code> method
+can lead to problems over the lifecycle of your application. For example, if the
+user changes screen orientation while a license check is active, the application
+{@link android.content.Context} is destroyed. If your application does not
+properly close the LicenseChecker's IPC connection, your application will crash
+when the response is received. Similarly, if the user exits your application
+while a license check is in progress, your application will crash when the
+response is received, unless it has properly called the
+LicenseChecker's <code>onDestroy()</code> method to disconnect from the service.
+</p>
+
+<p>Here's an example from the sample application included in the LVL, where
+<code>mChecker</code> is the LicenseChecker instance:</p>
+
+<pre> &#64;Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mChecker.onDestroy();
+ ...
+ }
+</pre>
+
+<p>If you are extending or modifying LicenseChecker, you might also need to call
+the LicenseChecker's <code>finishCheck()</code> method, to clean up any open IPC
+connections.</p>
+
+<h3 id="impl-DeviceLimiter">Implementing a DeviceLimiter</h3>
+
+<p>In some cases, you might want your Policy to limit the number of actual
+devices that are permitted to use a single license. This would prevent a user
+from moving a licensed application onto a number of devices and using the
+application on those devices under the same account ID. It would also prevent a
+user from "sharing" the application by providing the account information
+associated with the license to other individuals, who could then sign in to that
+account on their devices and access the license to the application. </p>
+
+<p>The LVL supports per-device licensing by providing a
+<code>DeviceLimiter</code> interface, which declares a single method,
+<code>allowDeviceAccess()</code>. When a LicenseValidator is handling a response
+from the licensing server, it calls <code>allowDeviceAccess()</code>, passing a
+user ID string extracted from the response.</p>
+
+<p>If you do not want to support device limitation, <strong>no work is
+required</strong> &mdash; the LicenseChecker class automatically uses a default
+implementation called NullDeviceLimiter. As the name suggests, NullDeviceLimiter
+is a "no-op" class whose <code>allowDeviceAccess()</code> method simply returns
+a <code>LICENSED</code> response for all users and devices. </p>
+
+<div style="border-left:4px solid #FFCF00;margin:1em;padding: 0 0 0 .5em">
+<p><strong>Caution:</strong> Per-device licensing is <em>not recommended for
+most applications</em> because:</p>
+<ul>
+<li>It requires that you provide a backend server to manage a users and devices
+mapping, and </li>
+<li>It could inadvertently result in a user being denied access to an
+application that they have legitimately purchased on another device.</li>
+</ul>
+</div>
+
+
+<h2 id="test-env">Setting Up the Testing Environment</h2>
+
+<p>The Android Market publisher site provides configuration tools that let you
+and others test licensing on your application before it is published. As you are
+implementing licensing, you can make use of the publisher site tools to test
+your application's Policy and handling of different licensing responses and
+error conditions.</p>
+
+<p>The main components of the test environment for licensing include: </p>
+
+<ul>
+<li>A "Test response" configuration in your publisher account that lets you
+set the static licensing response returned, when the server processes a
+license check for an application uploaded to the publisher account, from a user
+signed in to the publisher account or a test account.</li>
+<li>An optional set of test accounts that will receive the static test
+response when they check the license of an application that you have uploaded
+(regardless whether the application is published or not).</li>
+<li>A runtime environment for the application that includes the Android Market
+application or Google APIs Add-On, on which the user is signed in to the
+publisher account or one of the test accounts.</li>
+</ul>
+
+<p>Setting up the test environment properly involves:</p>
+
+<ol>
+<li><a href="#test-response">Setting static test responses</a> that are returned by the licensing server.</li>
+<li><a href="#test-acct-setup">Setting up test accounts</a> as needed.</li>
+<li><a href="#acct-signin">Signing in</a> properly to an emulator or device, before initiating a license check test.</li>
+</ol>
+
+<p>The sections below provide more information.</p>
+
+
+<h3 id="test-response">Setting test responses for license checks</h3>
+
+<p>Android Market provides a configuration setting in your publisher account
+that lets you override the normal processing of a license check and return a
+specified static response code. The setting is for testing only and applies
+<em>only</em> to license checks for applications that you have uploaded, made by
+any user signed in to an emulator or device using the credentials of the
+publisher account or a registered test account. For other users, the server
+always processes license checks according to normal rules. </p>
+
+<p>To set a test response for your account, sign in to your publisher account
+and click "Edit Profile". In the Edit Profile page, locate the Test Response
+menu in the Licensing panel, shown below. You can select from the full set of
+valid server response codes to control the response or condition you want to
+test in your application.</p>
+
+<p>In general, you should make sure to test your application's licensing
+implementation with every response code available in the Test Response menu.
+For a description of the codes, see <a href="#server-response-codes">Server
+Response Codes</a> in the Appendix of this document.</p>
+
+<div style="margin-bottom:2em;" id="licensing_test_response">
+
+<img src="{@docRoot}images/licensing_test_response.png" style="text-align:left;margin-bottom:0;" />
+<div style="margin:0 2em;padding:0"><strong>Figure 7.</strong> The Licensing
+panel of your account's Edit Profile page, showing the Test Accounts field and the
+Test Response menu.</div>
+</div>
+
+<p>Note that the test response that you configure applies account-wide &mdash;
+that is, it applies not to a single application, but to <em>all</em>
+applications associated with the publisher account. If you are testing multiple
+applications at once, changing the test response will affect all of those
+applications on their next license check (if the user is signed into
+the emulator or device using the publisher account or a test account).</p>
+
+<p>Before you can successfully receive a test response for a license check,
+you must sign in to the device or emulator on which the application
+is installed, and from which it is querying the server. Specifically, you must
+sign using either your publisher account or one of the test accounts that you
+have set up. For more information about test accounts, see the next section.</p>
+
+<p>See <a href="#server-response-codes">Server Response Codes</a> for a list of
+test responses available and their meanings. </p>
+
+
+<h3 id="test-acct-setup">Setting up test accounts</h3>
+
+<p>In some cases, you might want to let multiple teams of developers test
+licensing on applications that will ultimately be published through your
+publisher account, but without giving them access to your publisher account's
+sign-in credentials. To meet that need, the Android Market publisher site lets
+you set up one or more optional <em>test accounts</em> &mdash; accounts that are
+authorized to query the licensing server and receive static test responses from
+your publisher account.</p>
+
+<p>Test accounts are standard Google accounts that you register on your
+publisher account, such that they will receive the test response for
+applications that you have uploaded. Developers can then sign in to their
+devices or emulators using the test account credentials and initiate license
+checks from installed applications. When the licensing server receives a license
+check from a user of a test account, it returns the static test response
+configured for the publisher account. </p>
+
+<p>Necessarily, there are limitations on the access and permissions given to
+users signed in through test accounts, including:</p>
+
+<ul>
+<li>Test account users can query the licensing server only for applications that
+are already uploaded to the publisher account. </li>
+<li>Test account users do not have permission to upload applications to your
+publisher account.</li>
+<li>Test account users do not have permission to set the publisher account's
+static test response.</li>
+</ul>
+
+<p>The table below summarizes the differences in capabilities, between the
+publisher account, a test account, and any other account.</p>
+
+<p class="table-caption" id="acct-types-table"><strong>Table 1.</strong>
+Differences in account types for testing licensing.</p>
+
+<table>
+<tr>
+<th>Account Type</th>
+<th>Can check license before upload?</th>
+<th>Can receive test response?</th>
+<th>Can set test response?</th>
+</tr>
+
+<tr>
+<td>Publisher account</td>
+<td>Yes</td>
+<td>Yes</td>
+<td>Yes</td>
+</tr>
+
+<tr>
+<td>Test account</td>
+<td>No</td>
+<td>Yes</td>
+<td>No</td>
+</tr>
+
+<tr>
+<td>Other</td>
+<td>No</td>
+<td>No</td>
+<td>No</td>
+</tr>
+</table>
+
+<h4 id="reg-test-acct">Registering test accounts on the publisher account</h4>
+
+<p>To get started, you need to register each test account in your publisher
+account. As shown in <a href="#licensing_test_response">Figure 7</a>, above, you
+register test accounts in the Licensing panel of your publisher account's Edit
+Profile page. Simply enter the accounts as a comma-delimited list and click
+<strong>Save</strong> to save your profile changes.</p>
+
+<p>You can use any Google account as a test account. If you want to own and
+control the test accounts, you can create the accounts yourself and distribute
+the credentials to your developers or testers.</p>
+
+<h4 id="test-app-upload">Handling application upload and distribution for test
+account users</h4>
+
+<p>As mentioned above, users of test accounts can only receive static test
+responses for applications that are uploaded to the publisher account. Since
+those users do not have permission to upload applications, as the publisher you
+will need to work with those users to collect apps for upload and distribute
+uploaded apps for testing. You can handle collection and distribution in any way
+that is convenient. </p>
+
+<p>Once an application is uploaded and becomes known to the licensing server,
+developers and testers can continue modify the application in their local
+development environment, without having to upload new versions. You only need to
+upload a new version if the local application increments the
+<code>versionCode</code> attribute in the manifest file. </p>
+
+<h4 id="test-key">Distributing your public key to test account users</h4>
+
+<p>The licensing server handles static test responses in the normal way,
+including signing the license response data, adding extras parameters, and so
+on. To support developers who are implementing licensing using test accounts,
+rather than the publisher account, you will need to distribute
+your public key to them. Developers without access to the publisher site do not
+have access to your public key, and without the key they won't be able to
+verify license responses. </p>
+
+<p>Note that if you decide to generate a new licensing key pair for your account
+for some reason, you need to notify all users of test accounts. For
+testers, you can embed the new key in the application package and distribute it
+to users. For developers, you will need to distribute the new key to them
+directly. </p>
+
+
+<h3 id="acct-signin">Signing in to an authorized account in the runtime
+environment</h3>
+
+<p>The licensing service is designed to determine whether a given user is
+licensed to use a given application &mdash; during a license check, the Android
+Market application gathers the user ID from the primary account on the system
+and sends it to the server, together with the package name of the application
+and other information. However, if there is no user information available, the
+license check cannot succeed, so the Android Market application terminates the
+request and returns an error to the application. </p>
+
+<p>During testing, to ensure that your application can successfully query the
+licensing server, you must make sure that you sign in to an account <em>on the
+device or emulator</em> using:</p>
+
+<ul>
+<li>The credentials of a publisher account, or</li>
+<li>The credentials of a test account that is registered with a publisher
+account</li>
+</ul>
+
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Signing in to a Google account on an emulator</h2>
+
+<p>If you are testing licensing on an emulator, you need to sign in to a Google
+account on the emulator. If you do not see an option to create a new Google
+account, the problem might be that your AVD is running a standard Android system
+image, rather than the Google APIs Add-On, API 8 (release 2) or higher. </p>
+
+<p style="margin-top:.5em;">For more information, see <a
+href="#runtime-setup">Setting up the runtime environment</a>, above.</p>
+
+</div>
+</div>
+
+<p>Signing in using a publisher account offers the advantage of letting your
+applications receive static test responses even before the applications are
+uploaded to the publisher site.</p>
+
+<p>If you are part of a larger organization or are working with external groups
+on applications that will be published through your site, you will more likely
+want to distribute test accounts instead, then use those to sign in during
+testing. </p>
+
+<p>To sign in on a device or emulator, follow the steps below. The preferred
+approach is to sign in as the primary account &mdash; however, if there are
+other accounts already in use on the device or emulator, you can create an
+additional account and sign in to it using the publisher or test account
+credentials. </p>
+
+<ol>
+<li>Open Settings &gt; Accounts &amp; sync</li>
+<li>Select <strong>Add Account</strong> and choose to add a "Google" account.
+</li>
+<li>Select <strong>Next</strong> and then <strong>Sign in</strong>.</li>
+<li>Enter the username and password of either the publisher account or a test
+account that is registered in the publisher account.</li>
+<li>Select <strong>Sign in</strong>. The system signs you in to the new
+account.</li>
+</ol>
+
+<p>Once you are signed in, you can begin testing licensing in your application
+(if you have completed the LVL integration steps above). When your application
+initiates a license check, it will receive a response containing the static test
+response configured on the publisher account. </p>
+
+<p>Note that, if you are using an emulator, you will need to sign in to the
+publisher account or test account each time you wipe data when restarting the
+emulator.</p>
+
+<div style="margin:2em 1em 1em 1em;">
+
+<img src="{@docRoot}images/licensing_device_signin.png" style="text-align:left;" />
+<div style="margin:.25em 1.25em;padding:0"><strong>Figure 8.</strong> Example of
+setting up a Google account on a device or emulator.</div>
+</div>
+
+<h2 id="app-obfuscation">Obfuscating Your Application</h2>
+
+<p>To ensure the security of your application, particularly for a paid
+application that uses licensing and/or custom constraints and protections, it's
+very important to obfuscate your application code. Properly obfuscating your
+code makes it more difficult for a malicious user to decompile the application's
+bytecode, modify it &mdash; such as by removing the license check &mdash;
+and then recompile it.</p>
+
+<p>Several obfuscator programs are available for Android applications, including
+<a href="http://proguard.sourceforge.net/">ProGuard</a>, which also offers
+code-optimization features. The use of ProGuard or a similar program to obfuscate
+your code is <em>strongly recommended</em> for all applications that use Android
+Market Licensing. </p>
+
+<h2 id="app-publishing">Publishing a Licensed Application</h2>
+
+<p>When you are finished testing your license implementation, you are ready to
+publish the application on Android Market. Follow the normal steps to <a
+href="{@docRoot}guide/publishing/preparing.html">prepare</a>, <a
+href="{@docRoot}guide/publishing/app-signing.html">sign</a>, and then <a
+href="{@docRoot}guide/publishing/publishing.html">publish the application</a>.
+</p>
+
+<h4>Removing Copy Protection</h4>
+
+<p>After uploading your licensed application, remember to remove copy protection
+from the application, if it is currently used. To check and remove copy
+protection, sign in to the publisher site and go the application's upload
+details page. In the Publishing options section, make sure that the Copy
+Protection radio button selection is "Off".</p>
+
+<h4>Considerations for Free Apps</h4>
+
+<p>Licensing is currently supported only for paid applications. If you already
+published your application as free, you won't be able to upload an updated
+version that includes licensing (that is, an application that uses the same
+package name and that includes the <a href="#manifest-permission">licensing
+permission</a>). Here are some points to keep in mind:</p>
+
+<ul>
+<li>If you want to offer a free version of your application that provides a
+reduced feature set (or that offers the full feature set for trial period), the
+free version of your application must not include the licensing permission and
+must use a different package name than the paid version of the app.</li>
+<li>If you want to offer a paid version of your free application that uses
+licensing, you can do so under a new package name.</li>
+</ul>
+
+<h2 id="support">Where to Get Support</h2>
+
+<p>If you have questions or encounter problems while implementing or deploying
+publishing in your applications, please use the support resources listed in the
+table below. By directing your queries to the correct forum, you can get the
+support you need more quickly. </p>
+
+<p class="table-caption"><strong>Table 2.</strong> Developer support resources
+for Android Market Licensing Service.</p>
+
+<table>
+
+<tr>
+<th>Support Type</th>
+<th>Resource</th>
+<th>Range of Topics</th>
+</tr>
+<tr>
+<td rowspan="2">Development and testing issues</td>
+<td>Google Groups: <a
+href="http://groups.google.com/group/android-developers">android-developers</a>
+</td>
+<td rowspan="2">LVL download and integration, library projects, Policy
+questions, user experience ideas, handling of responses, Obfuscator, IPC, test
+environment setup</td>
+</tr>
+<tr>
+<td>Stack Overflow: <a
+href="http://stackoverflow.com/questions/tagged/android">http://stackoverflow.com/questions/tagged/android</a></td>
+</tr>
+<tr>
+<td rowspan="2">Accounts, publishing, and deployment issues</td>
+<td><a href="http://www.google.com/support/forum/p/Android+Market">Android
+Market Help Forum</a></td>
+<td rowspan="2">Publisher accounts, licensing key pair, test accounts, server
+responses, test responses, application deployment and results</td>
+</tr>
+<tr>
+<td><a
+href="http://market.android.com/support/bin/answer.py?answer=186113">Market
+Licensing Support FAQ</a></td>
+</tr>
+<tr>
+<td>LVL issue tracker</td>
+<td><a href="http://code.google.com/p/marketlicensing/issues/">Marketlicensing
+project issue tracker</a></td>
+<td>Bug and issue reports related specifically to the LVL source code classes
+and interface implementations</td>
+</tr>
+
+</table>
+
+<p>For general information about how to post to the groups listed above, see <a
+href="{@docRoot}resources/community-groups.html">Developer Forums</a> document
+in the Resources tab.</p>
+
+<h2 id="lvl-summary">Summary of LVL Classes and Interfaces</h2>
+
+<p>The table below lists all of the source files in the License Verification
+Library (LVL) available through the Android SDK. All of the files are part of
+the <code>com.android.vending.licensing</code> package.</p>
+
+<p class="table-caption"><strong>Table A-1.</strong> Summary of LVL library
+classes and interfaces.</p>
+
+<div style="width:99%">
+<table width="100%">
+
+<tr>
+<th width="15%">Category</th>
+<th width="20%">Name</th>
+<th width="100%">Description</th>
+</tr>
+
+<tr>
+<td rowspan="2">License check and result</td>
+<td>LicenseChecker</td>
+<td>Class that you instantiate (or subclass) to initiate a license check.</td>
+</tr>
+<tr>
+<td><em>LicenseCheckerCallback</em></td>
+<td>Interface that you implement to handle result of the license check.</td>
+</tr>
+
+<tr>
+<td rowspan="3" width="15%">Policy</td>
+<td width="20%"><em>Policy</em></td>
+<td width="100%">Interface that you implement to determine whether to allow
+access to the application, based on the license response. </td>
+</tr>
+<tr>
+<td>ServerManagedPolicy</td>
+<td width="100%">Default Policy implementation. Uses settings provided by the
+licensing server to manage local storage of license data, license validity,
+retry.</td>
+</tr>
+<tr>
+<td>StrictPolicy</td>
+<td>Alternative Policy implementation. Enforces licensing based on a direct
+license response from the server only. No caching or request retry.</td>
+</tr>
+
+<tr>
+<td rowspan="2" width="15%">Data obfuscation <br><em>(optional)</em></td>
+<td width="20%"><em>Obfuscator</em></td>
+<td width="100%">Interface that you implement if you are using a Policy (such as
+ServerManagedPolicy) that caches license response data in a persistent store.
+Applies an obfuscation algorithm to encode and decode data being written or
+read.</td>
+</tr>
+<tr>
+<td>AESObfuscator</td>
+<td>Default Obfuscator implementation that uses AES encryption/decryption
+algorithm to obfuscate/unobfuscate data.</td>
+</tr>
+
+<tr>
+<td rowspan="2" width="15%">Device limitation<br><em>(optional)</em></td>
+<td width="20%"><em>DeviceLimiter</em></td>
+<td width="100%">Interface that you implement if you want to restrict use of an
+application to a specific device. Called from LicenseValidator. Implementing
+DeviceLimiter is not recommended for most applications because it requires a
+backend server and may cause the user to lose access to licensed applications,
+unless designed with care.</td>
+</tr>
+<tr>
+<td>NullDeviceLimiter</td>
+<td>Default DeviceLimiter implementation that is a no-op (allows access to all
+devices).</td>
+</tr>
+
+<tr>
+<td rowspan="6" width="15%">Library core, no integration needed</td>
+<td width="20%">ResponseData</td>
+<td width="100%">Class that holds the fields of a license response.</td>
+</tr>
+<tr>
+<td>LicenseValidator</td>
+<td>Class that decrypts and verifies a response received from the licensing
+server.</td>
+</tr>
+<tr>
+<td>ValidationException</td>
+<td>Class that indicates errors that occur when validating the integrity of data
+managed by an Obfuscator.</td>
+</tr>
+<tr>
+<td>PreferenceObfuscator</td>
+<td>Utility class that writes/reads obfuscated data to the system's
+{@link android.content.SharedPreferences} store.</td>
+</tr>
+<tr>
+<td><em>ILicensingService</em></td>
+<td>One-way IPC interface over which a license check request is passed to the
+Android Market client.</td>
+</tr>
+<tr>
+<td><em>ILicenseResultListener</em></td>
+<td>One-way IPC callback implementation over which the application receives an
+asynchronous response from the licensing server.</td>
+</tr>
+
+</table>
+</div>
+
+
+<h2 id="server-response-codes">Server Response Codes</h2>
+
+<p>The table below lists all of the license response codes supported by the
+licensing server. In general, an application should handle all of these response
+codes. By default, the LicenseValidator class in the LVL provides all of the
+necessary handling of these response codes for you. </p>
+
+<p class="table-caption"><strong>Table A-2.</strong> Summary of response codes
+returned by the Android Market server in a license response.</p>
+
+<table>
+
+<tr>
+<th>Response Code</th>
+<th>Description</th>
+<th>Signed?</th>
+<th>Extras</th>
+<th>Comments</th>
+</tr>
+<tr>
+<td>LICENSED</td>
+<td>The application is licensed to the user. The user has purchased the
+application or the application is free.</td>
+<td>Yes</td>
+<td><code>VT</code>,&nbsp;<code>GT</code>, <code>GR</code></td>
+<td><em>Allow access according to Policy constraints.</em></td>
+</tr>
+<tr>
+<td>LICENSED_OLD_KEY</td>
+<td>The application is licensed to the user, but there is an updated application
+version available that is signed with a different key. </td>
+<td>Yes </td>
+<td><code>VT</code>, <code>GT</code>, <code>GR</code>, <code>UT</code></td>
+<td><em>Optionally allow access according to Policy constraints.</em>
+<p style="margin-top:.5em;">Can indicate that the key pair used by the installed
+application version is invalid or compromised. The application can allow access
+if needed or inform the user that an upgrade is available and limit further use
+until upgrade.</p>
+</td>
+</tr>
+<tr>
+<td>NOT_LICENSED</td>
+<td>The application is not licensed to the user.</td>
+<td>No</td>
+<td></td>
+<td><em>Do not allow access.</em></td>
+</tr>
+<tr>
+<td>ERROR_CONTACTING_SERVER</td>
+<td>Local error &mdash; the Android Market application was not able to reach the
+licensing server, possibly because of network availability problems. </td>
+<td>No</td>
+<td></td>
+<td><em>Retry the license check according to Policy retry limits.</em></td>
+</tr>
+<tr>
+<td>ERROR_SERVER_FAILURE</td>
+<td>Server error &mdash; the server could not load the publisher account's key
+pair for licensing.</td>
+<td>No</td>
+<td></td>
+<td><em>Retry the license check according to Policy retry limits.</em>
+</td>
+</tr>
+<tr>
+<td>ERROR_INVALID_PACKAGE_NAME</td>
+<td>Local error &mdash; the application requested a license check for a package
+that is not installed on the device. </td>
+<td>No </td>
+<td></td>
+<td><em>Do not retry the license check.</em>
+<p style="margin-top:.5em;">Typically caused by a development error.</p>
+</td>
+</tr>
+<tr>
+<td>ERROR_NON_MATCHING_UID</td>
+<td>Local error &mdash; the application requested a license check for a package
+whose UID (package, user ID pair) does not match that of the requesting
+application. </td>
+<td>No </td>
+<td></td>
+<td><em>Do not retry the license check.</em>
+<p style="margin-top:.5em;">Typically caused by a development error.</p>
+</td>
+</tr>
+<tr>
+<td>ERROR_NOT_MARKET_MANAGED</td>
+<td>Server error &mdash; the application (package name) was not recognized by
+Android Market. </td>
+<td>No</td>
+<td></td>
+<td><em>Do not retry the license check.</em>
+<p style="margin-top:.5em;">Can indicate that the application was not published
+through Android Market or that there is an development error in the licensing
+implementation.</p>
+</td>
+</tr>
+
+</table>
+
+
+<h2 id="extras">Server Response Extras</h2>
+
+<p>The licensing server includes several settings in certain types of license
+responses, to assist the application and its Policy in managing access to the
+application across the 24-hour refund period and other conditions. Specifically,
+the server provides recommended values for the application's license validity
+period, retry grace period, maximum allowable retry count, and other settings.
+The server appends the settings as key-value pairs in the license response
+"extras" field. </p>
+
+<p>Any Policy implementation can extract the extras settings from the license
+response and use them as needed. The LVL default Policy implementation, <a
+href="#ServerManagedPolicy">ServerManagedPolicy</a>, serves as a working
+implementation and an illustration of how to obtain, store, and use the
+settings. </p>
+
+<p class="table-caption"><strong>Table A-3.</strong> Summary of
+license-management settings supplied by the Android Market server in a license
+response.</p>
+
+<table>
+<tr>
+<th>Extra</th><th>Description</th>
+</tr>
+
+<tr>
+ <td>VT</td>
+ <td>License validity timestamp. Specifies the date/time at which the current
+(cached) license response expires and must be rechecked on the licensing server.
+ </td>
+</tr>
+<tr>
+ <td>GT</td>
+ <td>Grace period timestamp. Specifies the end of the period during which a
+Policy may allow access to the application, even though the response status is
+RETRY. <p>The value is managed by the server, however a typical value would be 5
+or more days.</p></td>
+</tr>
+<tr>
+ <td>GR</td>
+ <td>Maximum retries count. Specifies how many consecutive RETRY license checks
+the Policy should allow, before denying the user access to the application.
+<p>The value is managed by the server, however a typical value would be "10" or
+higher.</p></td>
+</tr>
+<tr>
+ <td>UT</td>
+ <td>Update timestamp. Specifies the day/time when the most recent update to
+this application was uploaded and published. <p>The server returns this extra
+only for LICENSED_OLD_KEYS responses, to allow the Policy to determine how much
+time has elapsed since an update was published with new licensing keys before
+denying the user access to the application. </p></td>
+</tr>
+
+</table>
+
+<p>The sections below provide more information about the server-provided
+settings and how to use them. </p>
+
+<h4>License validity period</h4>
+
+<p>The Android Market licensing server sets a license validity period for all
+downloaded applications. The period expresses the interval of time over which an
+application's license status should be considered as unchanging and cacheable by
+a licensing Policy in the application. The licensing server includes the
+validity period in its response to all license checks, appending an
+end-of-validity timestamp to the response as an extra under the key "VT". A
+Policy can extract the VT key value and use it to conditionally allow access to
+the application without rechecking the license, until the validity period
+expires. </p>
+
+<p>The license validity signals to a licensing Policy when it must recheck the
+licensing status with the licensing server. It is <em>not</em> intended to imply
+whether an application is actually licensed for use. That is, when an
+application's license validity period expires, this does not mean that the
+application is no longer licensed for use &mdash; rather, it indicates only that
+the Policy must recheck the licensing status with the server. It follows that,
+as long as the license validity period is not expired, it is acceptable for the
+Policy to cache the initial license status locally and return the cached license
+status instead of sending a new license check to the server.</p>
+
+<p>The licensing server manages the validity period as a means of helping the
+application properly enforce licensing across the refund period offered by
+Android Market for paid applications. It sets the validity period based on
+whether the application was purchased and, if so, how long ago. Specifically,
+the server sets a validity period as follows:</p>
+
+<ul>
+<li>For a paid application, the server sets the initial license validity period
+so that the license response remains valid for as long as the application is
+refundable. A licensing Policy in the application may cache the
+result of the initial license check and does not need to recheck the license
+until the validity period has expired.</li>
+<li>When an application is no longer refundable, the server
+sets a longer validity period &mdash; typically a number of days. </li>
+<li>For a free application, the server sets the validity period to a very high
+value (<code>long.MAX_VALUE</code>). This ensures that, provided the Policy has
+cached the validity timestamp locally, it will not need to recheck the
+license status of the application in the future.</li>
+</ul>
+
+<p>The ServerManagedPolicy implementation uses the extracted timestamp
+(<code>mValidityTimestamp</code>) as a primary condition for determining whether
+to recheck the license status with the server before allowing the user access to
+the application. </p>
+
+<h4>Retry period and maximum retry count</h4>
+
+<p>In some cases, system or network conditions can prevent an application's
+license check from reaching the licensing server, or prevent the server's
+response from reaching the Android Market client application. For example, the
+user might launch an application when there is no cell network or data
+connection available &mdash; such as when on an airplane &mdash; or when the
+network connection is unstable or the cell signal is weak. </p>
+
+<p>When network problems prevent or interrupt a license check, the Android
+Market client notifies the application by returning a "RETRY" response code to
+the Policy's <code>processServerResponse()</code> method. In the case of system
+problems, such as when the application is unable to bind with Android Market's
+ILicensingService implementation, the LicenseChecker library itself calls the
+Policy <code>processServerResonse()</code> method with a "RETRY" response code.
+</p>
+
+<p>In general, the RETRY response code is a signal to the application that an
+error has occurred that has prevented a license check from completing.
+
+<p>The Android Market server helps an application to manage licensing under
+error conditions by setting a retry "grace period" and a recommended maximum
+retries count. The server includes these values in all license check responses,
+appending them as extras under the keys "GT" and "GR". </p>
+
+<p>The application Policy can extract the GT and GR extras and use them to
+conditionally allow access to the application, as follows:</p>
+
+<ul>
+<li>For a license check that results in a RETRY response, the Policy should
+cache the RETRY response code and increment a count of RETRY responses.</li>
+<li>The Policy should allow the user to access the application, provided that
+either the retry grace period is still active or the maximum retries count has
+not been reached.</li>
+</ul>
+
+<p>The ServerManagedPolicy uses the server-supplied GT and GR values as
+described above. The example below shows the conditional handling of the retry
+responses in the <code>allow()</code> method. The count of RETRY responses is
+maintained in the <code>processServerResponse()</code> method, not shown. </p>
+
+
+<pre> public boolean allowAccess() {
+ long ts = System.currentTimeMillis();
+ if (mLastResponse == LicenseResponse.LICENSED) {
+ // Check if the LICENSED response occurred within the validity timeout.
+ if (ts &lt;= mValidityTimestamp) {
+ // Cached LICENSED response is still valid.
+ return true;
+ }
+ } else if (mLastResponse == LicenseResponse.RETRY &amp;&amp;
+ ts &lt; mLastResponseTime + MILLIS_PER_MINUTE) {
+ // Only allow access if we are within the retry period or we haven't used up our
+ // max retries.
+ return (ts &lt;= mRetryUntil || mRetryCount &lt;= mMaxRetries);
+ }
+ return false;
+ }</pre>
+
diff --git a/docs/html/guide/publishing/preparing.jd b/docs/html/guide/publishing/preparing.jd
index c1c6351..442c12a 100644
--- a/docs/html/guide/publishing/preparing.jd
+++ b/docs/html/guide/publishing/preparing.jd
@@ -39,13 +39,14 @@ Applications</a> document. </p>
<ol>
<li>Test your application extensively on an actual device </li>
<li>Consider adding an End User License Agreement in your application</li>
+<li>Consider adding licensing support</li>
<li>Specify an icon and label in the application's manifest</li>
<li>Turn off logging and debugging and clean up data/files</li>
</ol>
<p>Before you do the final compile of your application:</p>
-<ol start="5">
+<ol start="6">
<li>Version your application</li>
<li>Obtain a suitable cryptographic key</li>
<li>Register for a Maps API Key, if your application is using MapView elements</li>
@@ -53,7 +54,7 @@ Applications</a> document. </p>
<p><em>Compile your application...</em></p>
<p>After compiling your application:</p>
-<ol start="8">
+<ol start="9">
<li>Sign your application</li>
<li>Test your compiled application</li>
</ol>
@@ -101,7 +102,19 @@ application</h3>
<p>To protect your person, organization, and intellectual property, you may want
to provide an End User License Agreement (EULA) with your application.
-<h3 id="iconlabel">3. Specify an icon and label in the application's manifest</h3>
+<h3 id="eula">3. Consider adding support for Android Market Licensing</h3>
+
+<p>If you are publishing a paid application through Android Market, consider
+adding support for Android Market Licensing. Licensing lets you control access
+to your application based on whether the current user has purchased it.
+Using Android Market Licensing is optional.
+
+<p>For complete information about Android Market Licensing Service and how to
+use it in your application, see <a
+href="{@docRoot}guide/publishing/licensing.html">Licensing Your
+Applications</a>.</p>
+
+<h3 id="iconlabel">4. Specify an icon and label in the application's manifest</h3>
<p>The icon and label that you specify in an application's manifest are
important because they are displayed to users as your application's icon and
@@ -116,7 +129,7 @@ display the icon and label to users. </p>
<p>As regards the design of your icon, you should try to make it match as much
as possible the style used by the built-in Android applications.</p>
-<h3 id="logging">4. Turn off logging and debugging and clean up data/files</h3>
+<h3 id="logging">5. Turn off logging and debugging and clean up data/files</h3>
<p>For release, you should make sure that debug facilities are turned off and
that debug and other unnecessary data/files are removed from your application
@@ -133,7 +146,7 @@ code.</li>
<h2 id="finalcompile">Before you do the final compile of your application</h2>
-<h3 id="versionapp">5. Version your application</h3>
+<h3 id="versionapp">6. Version your application</h3>
<p>Before you compile your application, you must make sure that you have defined
a version number for your application, specifying an appropriate value for both
@@ -152,7 +165,7 @@ element in the application's manifest file, using appropriate values. </p>
application, see <a href="{@docRoot}guide/publishing/versioning.html">Versioning
Your Applications</a>.</p>
-<h3 id="cryptokey">6. Obtain a suitable cryptographic key</h3>
+<h3 id="cryptokey">7. Obtain a suitable cryptographic key</h3>
<p>If you have read and followed all of the preparation steps up to this point,
your application is compiled and ready for signing. Inside the .apk, the
@@ -173,7 +186,7 @@ elements.</li>
<li>Sign your application for release, later in the preparation process</li>
</ul>
-<h3 id="mapsApiKey">7. Register for a Maps API Key, if your application is using
+<h3 id="mapsApiKey">8. Register for a Maps API Key, if your application is using
MapView elements</h3>
<div class="sidebox-wrapper">
@@ -231,7 +244,7 @@ you can compile your application for release.</p>
<h2 id="post-compile">After compiling your application</h2>
-<h3 id="signapp">8. Sign your application</h3>
+<h3 id="signapp">9. Sign your application</h3>
<p>Sign your application using your private key and then
align it with the {@code zipalign} tool. Signing your application
@@ -239,7 +252,7 @@ correctly is critically important. Please see
<a href="{@docRoot}guide/publishing/app-signing.html">Signing Your
Applications</a> for complete information. </p>
-<h3 id="testapp">9. Test your compiled and signed application</h3>
+<h3 id="testapp">10. Test your compiled and signed application</h3>
<p>Before you release your compiled application, you should thoroughly test it
on the target mobile device (and target network, if possible). In particular,
diff --git a/docs/html/guide/publishing/publishing.jd b/docs/html/guide/publishing/publishing.jd
index 0c087ef..9b470c8 100644
--- a/docs/html/guide/publishing/publishing.jd
+++ b/docs/html/guide/publishing/publishing.jd
@@ -18,7 +18,8 @@ page.title=Publishing Your Applications
<ol>
<li><a href="#overview">Publishing on Android Market</a>
<ol>
- <li><a href="#marketupgrade">Publishing Updates on Android Market</a>
+ <li><a href="#marketupgrade">Publishing Updates on Android Market</a></li>
+ <li><a href="#marketLicensing">Using Android Market Licensing Service</a></li>
<li><a href="#marketintent">Using Intents to Launch the Market Application</a></li>
</ol></li>
<!--
@@ -30,6 +31,7 @@ page.title=Publishing Your Applications
<h2>See also</h2>
<ol>
+<li><a href="{@docRoot}guide/publishing/licensing.html">Licensing Your Applications</a></li>
<li><a href="{@docRoot}guide/publishing/preparing.html">Preparing to Publish</a></li>
</ol>
@@ -59,7 +61,7 @@ the .apk. Your application is now ready for publishing. </p>
<p>The sections below provide information about publishing your Android
application to mobile device users.</p>
-<h2 id="market">Publishing on Android Market</h2>
+<h2 id="overview">Publishing on Android Market</h2>
<p>Android Market is a hosted service that makes it easy for users to find and
download Android applications to their Android-powered devices, and makes it
@@ -121,7 +123,26 @@ certificate do <em>not</em> match those of the existing version, Market will
consider it a new application and will not offer it to users as an update.</p>
+<h3 id="marketLicensing">Using Android Market Licensing Service</h3>
+<p>Android Market offers a licensing service that lets you enforce licensing
+policies for paid applications that you publish through Android Market. With
+Android Market Licensing, your applications can query Android Market at run time
+to obtain their licensing status for the current user, then allow or disallow
+further use as appropriate. Using the service, you can apply a flexible
+licensing policy on an application-by-application basis &mdash; each
+application can enforce its licensing status in the way most appropriate
+for it. </p>
+
+<p>Any application that you publish through Android Market can use the Android
+Market Licensing Service. The service uses no dedicated framework APIs, you can
+add licensing to any legacy application that uses a minimum API level of 3 or
+higher.</p>
+
+<p>For complete information about Android Market Licensing Service and how to
+use it in your application, see <a
+href="{@docRoot}guide/publishing/licensing.html">Licensing Your
+Applications</a>.</p>
<h3 id="marketintent">Using Intents to Launch the Market Application on
diff --git a/docs/html/guide/samples/index.jd b/docs/html/guide/samples/index.jd
index 2f3ac5e..bd9ea52 100644
--- a/docs/html/guide/samples/index.jd
+++ b/docs/html/guide/samples/index.jd
@@ -3,99 +3,13 @@ page.title=Sample Code
@jd:body
-<p>Sometimes, the best way to learn how things are done is to look at some code.
-Here, you can browse the source of some sample Android applications that are included
-in the Android SDK.</p>
+<script type="text/javascript">
+ window.location = toRoot + "resources/samples/index.html";
+</script>
-<p>Each version of the Android platform available for the SDK includes a full set of sample
-applications (which may vary between different versions of the platform).
-You can find the samples in your SDK at:</p>
+<p><strong>This document has moved. Please go to <a
+href="http://developer.android.com/resources/samples/index.html">List of Sample
+Apps</a>.</strong></p>
-<p style="margin-left:2em">
-<code><em>&lt;sdk&gt;</em>/platforms/android-<em>&lt;version&gt;</em>/samples/</code>
-</p>
-
-<p>You can easily create new Android projects with these samples, modify them
-if you'd like, then run them on an emulator or device. For example, to create
-a project for the API Demos app from Eclipse,
-start a new Android Project, select "Create project from existing source", then select
-{@code ApiDemos} in the {@code samples/} directory. To create the API Demos project
-using the {@code android} tool, execute:</p>
-<pre>
-android update project -s -n API Demos -t <em>&lt;target_ID></em> -p <em>&lt;path-to-platform></em>/samples/ApiDemos/
-</pre>
-
-<p>The pages below provide an overview of each sample application (available with most
-platforms) and allow you to view the source files in your browser. </p>
-
-<div class="special">
- <p>Some of the samples in this listing are not yet available in the
- SDK. While we work to update the SDK, you can
- <a href="{@docRoot}shareables/latest_samples.zip">download the latest samples</a> as a ZIP
- archive.</p>
-</div>
-
-<dl>
-
- <dt><a href="{@docRoot}resources/samples/ApiDemos/index.html">API Demos</a></dt>
- <dd>A variety of small applications that demonstrate an extensive collection of
- framework topics.</dd>
-
- <dt><a href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a></dt>
- <dd>An simple example that illustrates a few different ways for an application to
- implement support for the Android data backup and restore mechanism.</dd>
-
- <dt><a href="{@docRoot}resources/samples/BluetoothChat/index.html">Bluetooth Chat</a></dt>
- <dd>An application for two-way text messaging over Bluetooth.</dd>
-
- <dt><a href="{@docRoot}resources/samples/ContactManager/index.html">Contact Manager</a></dt>
- <dd>An application that demonstrates how to query the system contacts provider
- using the <code>ContactsContract</code> API, as
- well as insert contacts into a specific account.</dd>
-
- <dt><a href="{@docRoot}resources/samples/Home/index.html">Home</a></dt>
- <dd>A home screen replacement application.</dd>
-
- <dt><a href="{@docRoot}resources/samples/JetBoy/index.html">JetBoy</a></dt>
- <dd>JetBoy is a game that demonstrates the SONiVOX JET interactive music technology,
- with {@link android.media.JetPlayer}.</dd>
-
- <dt><a href="{@docRoot}resources/samples/LunarLander/index.html">Lunar Lander</a></dt>
- <dd>A classic Lunar Lander game.</dd>
-
- <dt><a href="{@docRoot}resources/samples/MultiResolution/index.html">Multiple Resolutions</a></dt>
- <dd>A sample application that shows how to use resource directory qualifiers to
- provide different resources for different screen configurations.</dd>
-
- <dt><a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a></dt>
- <dd>An application for saving notes. Similar (but not identical) to the
- <a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad tutorial</a>.</dd>
-
- <dt><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary</a></dt>
- <dd>A sample application that demonstrates Android's search framework,
- including how to provide search suggestions for Quick Search Box.</dd>
-
- <dt><a href="{@docRoot}resources/samples/Snake/index.html">Snake</a></dt>
- <dd>An implementation of the classic game "Snake."</dd>
-
- <dt><a href="{@docRoot}resources/samples/SoftKeyboard/index.html">Soft Keyboard</a></dt>
- <dd>An example of writing an input method for a software keyboard.</dd>
-
- <dt><a href=""{@docRoot}resources/samples/Wiktionary/index.html">Wiktionary</a></dt>
- <dd>An example of creating interactive widgets for display on the Android
- home screen.</dd>
-
- <dt><a href="{@docRoot}resources/samples/WiktionarySimple/index.html">Wiktionary (Simplified)</a></dt>
- <dd>A simple Android home screen widgets example.</dd>
-
-</dl>
-
-
-<div class="special">
-<p>For more sample applications, check out
-<a href="http://code.google.com/p/apps-for-android/">apps-for-android</a>, a
-collection of open source applications that demonstrate various Android APIs.
-</p>
-</div>
diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd
index 4e74a83..6c02031 100644
--- a/docs/html/guide/topics/data/backup.jd
+++ b/docs/html/guide/topics/data/backup.jd
@@ -33,7 +33,7 @@ page.title=Data Backup
<li><a href="#RestoreVersion">Checking the Restore Data Version</a></li>
<li><a href="#RequestingBackup">Requesting Backup</a></li>
<li><a href="#RequestingRestore">Requesting Restore</a></li>
- <li><a href="#DevelopingTesting">Developing and Testing Your Backup Agent</a></li>
+ <li><a href="#Testing">Testing Your Backup Agent</a></li>
</ol>
<h2>Key classes</h2>
@@ -43,6 +43,11 @@ page.title=Data Backup
<li>{@link android.app.backup.BackupAgentHelper}</li>
</ol>
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr} tool</a></li>
+ </ol>
+
</div>
</div>
@@ -235,7 +240,7 @@ Backup Service Key is ignored.</p>
<h2 id="BackupAgent">Extending BackupAgent</h2>
<p>Most applications shouldn't need to extend the {@link android.app.backup.BackupAgent} class
-directly, but should instead <a href="BackupAgentHelper">extend BackupAgentHelper</a> to take
+directly, but should instead <a href="#BackupAgentHelper">extend BackupAgentHelper</a> to take
advantage of the built-in helper classes that automatically backup and restore your files. However,
you might want to extend {@link android.app.backup.BackupAgent} directly if you need to:</p>
<ul>
@@ -257,7 +262,7 @@ create your table and insert the data during a restore operation.</li>
<p>If you don't need to perform any of the tasks above and want to back up complete files from
{@link android.content.SharedPreferences} or <a
href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>, you
-should skip to <a href="BackupAgentHelper">Extending BackupAgentHelper</a>.</p>
+should skip to <a href="#BackupAgentHelper">Extending BackupAgentHelper</a>.</p>
@@ -308,7 +313,7 @@ backup for all applications that have requested a backup since the last backup w
<p class="note"><strong>Tip:</strong> While developing your application, you can initiate an
immediate backup operation from the Backup Manager with the <a
-href="{@docRoot}guide/developing/tools/bmgr.html">bmgr tool</a>.</p>
+href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr} tool</a>.</p>
<p>When the Backup Manager calls your {@link
android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
@@ -452,7 +457,7 @@ android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore(
href="#RequestingRestore">Requesting restore</a> for more information).</p>
<p class="note"><strong>Note:</strong> While developing your application, you can also request a
-restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">bmgr
+restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}
tool</a>.</p>
<p>When the Backup Manager calls your {@link
@@ -571,8 +576,8 @@ helpers.</p>
<h3 id="SharedPreferences">Backing up SharedPreferences</h3>
-<p>When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must the
-name of one or more {@link android.content.SharedPreferences} files.</p>
+<p>When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must
+include the name of one or more {@link android.content.SharedPreferences} files.</p>
<p>For example, to back up a {@link android.content.SharedPreferences} file named
"user_preferences", a complete backup agent using {@link android.app.backup.BackupAgentHelper} looks
@@ -813,7 +818,7 @@ onBackup()}.</p>
<p class="note"><strong>Note:</strong> While developing your application, you can request a
backup and initiate an immediate backup operation with the <a
-href="{@docRoot}guide/developing/tools/bmgr.html">bmgr
+href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}
tool</a>.</p>
@@ -828,25 +833,52 @@ android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescripto
implementation, passing the data from the current set of backup data.</p>
<p class="note"><strong>Note:</strong> While developing your application, you can request a
-restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">bmgr
+restore operation with the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}
tool</a>.</p>
-<h2 id="DevelopingTesting">Developing and Testing Your Backup Agent</h2>
+<h2 id="Testing">Testing Your Backup Agent</h2>
-<p>To develop and test your backup agent:</p>
-<ul>
- <li>Set your build target to a platform using API Level 8 or higher</li>
- <li>Run your application on a suitable Android system image:
+<p>Once you've implemented your backup agent, you can test the backup and restore functionality
+with the following procedure, using <a
+href="{@docRoot}guide/developing/tools/bmgr.html">{@code bmgr}</a>.</p>
+
+<ol>
+ <li>Install your application on a suitable Android system image
<ul>
- <li>If using the emulator, create and use an AVD with the Google APIs add-on (API Level
-8) &mdash; the Google APIs add-on is available as an SDK component through the SDK and AVD
-Manager</li>
+ <li>If using the emulator, create and use an AVD with Android 2.2 (API Level 8).</li>
<li>If using a device, the device must be running Android 2.2 or greater and have Android
-Market built in</li>
+Market built in.</li>
</ul>
</li>
- <li>Test your backup agent using the <a href="{@docRoot}guide/developing/tools/bmgr.html">{@code
-bmgr}</a> tool to initiate backup and restore operations</li>
-</ul>
+ <li>Ensure that backup is enabled
+ <ul>
+ <li>If using the emulator, you can enable backup with the following command from your SDK
+{@code tools/} path:
+<pre class="no-pretty-print">adb shell bmgr enable true</pre>
+ </li>
+ <li>If using a device, open the system <b>Settings</b>, select <b>Privacy</b>, then enable
+<b>Back up my data</b> and <b>Automatic restore</b>.
+ </ul>
+ </li>
+ <li>Open your application and initialize some data
+ <p>If you've properly implemented backup in your application, then it should request a
+backup each time the data changes. For example, each time the user changes some data, your app
+should call {@link android.app.backup.BackupManager#dataChanged()}, which adds a backup request to
+the Backup Manager queue. For testing purposes, you can also make a request with the following
+{@code bmgr} command:</p>
+<pre class="no-pretty-print">adb shell bmgr backup <em>your.package.name</em></pre>
+ </li>
+ <li>Initiate a backup operation:
+<pre class="no-pretty-print">adb shell bmgr run</pre>
+ <p>This forces the Backup Manager to perform all backup requests that are in its
+queue.</p>
+ <li>Uninstall your application:
+<pre class="no-pretty-print">adb uninstall <em>your.package.name</em></pre>
+ </li>
+ <li>Re-install your application.</li>
+</ol>
+
+<p>If your backup agent is successful, all the data you initialized in step 4 is restored.</p>
+
diff --git a/docs/html/guide/topics/resources/available-resources.jd b/docs/html/guide/topics/resources/available-resources.jd
index 09c55a5..19babee 100644
--- a/docs/html/guide/topics/resources/available-resources.jd
+++ b/docs/html/guide/topics/resources/available-resources.jd
@@ -18,23 +18,6 @@ of application resource that you can provide in your resources directory ({@code
<p>Here's a brief summary of each resource type:</p>
-<div class="sidebox-wrapper">
-<div class="sidebox">
-<h2>{@code R.id} Is Not a Resource</h2>
-
-<p>You will often use an {@code R.id} integer to handle {@link android.view.View} objects in
-your UI. Although the {@code id} is a subclass of the {@code R} class, it is not considered a
-"resource" because it is not a reference to an externalized application resource. The {@code id}
-is simply a unique identifier that allows you to handle elements in your UI by instantiating
-objects with {@link android.app.Activity#findViewById(int) findViewById()}.</p>
-
-<p>For information about using {@code R.id} with your UI, see <a
-href="{@docRoot}guide/topics/ui/declaring-layout.html#attributes">Declaring Layout</a>.</p>
-
-</div>
-</div>
-
-
<dl>
<dt><a href="{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a></dt>
<dd>Define pre-determined animations.<br/>
diff --git a/docs/html/guide/topics/resources/color-list-resource.jd b/docs/html/guide/topics/resources/color-list-resource.jd
index 449b66f..b20915c 100644
--- a/docs/html/guide/topics/resources/color-list-resource.jd
+++ b/docs/html/guide/topics/resources/color-list-resource.jd
@@ -55,7 +55,6 @@ In XML: <code>@[<em>package</em>:]color/<em>filename</em></code>
android:state_pressed=["true" | "false"]
android:state_focused=["true" | "false"]
android:state_selected=["true" | "false"]
- android:state_active=["true" | "false"]
android:state_checkable=["true" | "false"]
android:state_checked=["true" | "false"]
android:state_enabled=["true" | "false"]
diff --git a/docs/html/guide/topics/resources/drawable-resource.jd b/docs/html/guide/topics/resources/drawable-resource.jd
index d8de16a..035ddb9 100644
--- a/docs/html/guide/topics/resources/drawable-resource.jd
+++ b/docs/html/guide/topics/resources/drawable-resource.jd
@@ -12,38 +12,63 @@ parent.link=available-resources.html
</div>
</div>
-<p>A drawable resource is a general concept for a graphic that you
-can retrieve with {@link android.content.res.Resources#getDrawable(int)}
-and draw on the screen. There are several different types of drawables:</p>
+<p>A drawable resource is a general concept for a graphic that can be drawn to the screen and which
+you can retrieve with APIs such as {@link android.content.res.Resources#getDrawable(int)} or apply
+to another XML resource with attributes such as {@code android:drawable} and {@code android:icon}.
+There are several different types of drawables:</p>
<dl>
<dt><a href="#Bitmap">Bitmap File</a><dt>
<dd>A bitmap graphic file ({@code .png}, {@code .jpg}, or {@code .gif}).
- A {@link android.graphics.drawable.BitmapDrawable}.</dd>
+ Creates a {@link android.graphics.drawable.BitmapDrawable}.</dd>
<dt><a href="#NinePatch">Nine-Patch File</a></dt>
<dd>A PNG file with stretchable regions to allow image resizing based on content ({@code
-.9.png}). A {@link android.graphics.drawable.NinePatchDrawable}.</dd>
-<!-- <dt><a href="#BitmapAlias">Bitmap Alias</a><dt>
- <dd>An alias for a drawable.</dd> -->
+.9.png}). Creates a {@link android.graphics.drawable.NinePatchDrawable}.</dd>
+ <dt><a href="#LayerList">Layer List</a></dt>
+ <dd>A Drawable that manages an array of other Drawables. These are drawn in array order, so the
+element with the largest index is be drawn on top. Creates a {@link
+android.graphics.drawable.LayerDrawable}.</dd>
<dt><a href="#StateList">State List</a></dt>
<dd>An XML file that references different bitmap graphics
for different states (for example, to use a different image when a button is pressed).
- A {@link android.graphics.drawable.StateListDrawable}.</dd>
- <dt><a href="#Color">Color</a></dt>
- <dd>A resource defined in XML that specifies a rectangle of color, with
- optionally rounded corners. A {@link android.graphics.drawable.PaintDrawable}.</dd>
- <dt><a href="#Shape">Shape</a></dt>
+ Creates a {@link android.graphics.drawable.StateListDrawable}.</dd>
+ <dt><a href="#LevelList">Level List</a></dt>
+ <dd>An XML file that defines a Drawable that manages a number of alternate Drawables, each
+assigned a maximum numerical value. Creates a {@link
+android.graphics.drawable.LevelListDrawable}.</dd>
+ <dt><a href="#Transition">Transition Drawable</a></dt>
+ <dd>An XML file that defines a Drawable that can cross-fade between two drawable resources.
+Creates a {@link android.graphics.drawable.TransitionDrawable}.</dd>
+ <dt><a href="#Clip">Clip Drawable</a></dt>
+ <dd>An XML file that defines a drawable that clips another Drawable based on this Drawable's
+current level value. Creates a {@link android.graphics.drawable.ClipDrawable}.</dd>
+ <dt><a href="#Scale">Scale Drawable</a></dt>
+ <dd>An XML file that defines a drawable that changes the size of another Drawable based on its
+current level value. Creates a {@link android.graphics.drawable.ScaleDrawable}</dd>
+ <dt><a href="#Shape">Shape Drawable</a></dt>
<dd>An XML file that defines a geometric shape, including colors and gradients.
- A {@link android.graphics.drawable.ShapeDrawable}.</dd>
+ Creates a {@link android.graphics.drawable.ShapeDrawable}.</dd>
</dl>
-<p>Documentation for the {@link android.graphics.drawable.AnimationDrawable} resource
-is in the <a href="animation-resource.html">Animation Resource</a> document.</p>
+<p>Also see the <a href="animation-resource.html">Animation Resource</a> document for how to
+create an {@link android.graphics.drawable.AnimationDrawable}.</p>
+
+<p class="note"><strong>Note:</strong> A <a
+href="{@docRoot}guide/topics/resources/more-resources.html#Color">color resource</a> can also be
+used as a drawable in XML. For example, when creating a <a href="#StateList">state list
+drawable</a>, you can reference a color resource for the {@code android:drawable} attribute ({@code
+android:drawable="@color/green"}).</p>
+
+
+
-<h2 id="Bitmap">Bitmap File</h2>
+<h2 id="Bitmap">Bitmap</h2>
-<p>A basic bitmap image. Android supports basic bitmap files in a few different formats:
+<p>A bitmap image. Android supports bitmap files in a three formats:
{@code .png} (preferred), {@code .jpg} (acceptable), {@code .gif} (discouraged).</p>
+<p>You can reference a bitmap file directly, using the filename as the resource ID, or create an
+alias resource ID in XML.</p>
+
<p class="note"><strong>Note:</strong> Bitmap files may be automatically optimized with lossless
image compression by the <a href="{@docRoot}guide/developing/tools/aapt.html">aapt</a> tool. For
example, a true-color PNG that does not require more than 256 colors may be converted to an 8-bit
@@ -52,11 +77,18 @@ memory. So be aware that the image binaries placed in this directory can change
you plan on reading an image as a bit stream in order to convert it to a bitmap, put your images in
the <code>res/raw/</code> folder instead, where they will not be optimized.</p>
+
+<h3 id="BitmapFile">Bitmap File</h3>
+
+<p>A bitmap file is a {@code .png}, {@code .jpg}, or {@code .gif} file. Android creates a {@link
+android.graphics.drawable.Drawable}
+resource for any of these files when you save them in the {@code res/drawable/} directory.</p>
+
<dl class="xml">
<dt>file location:</dt>
<dd><code>res/drawable/<em>filename</em>.png</code> ({@code .png}, {@code .jpg}, or {@code .gif})<br/>
-The filename will be used as the resource ID.</dd>
+The filename is used as the resource ID.</dd>
<dt>compiled resource datatype:</dt>
<dd>Resource pointer to a {@link android.graphics.drawable.BitmapDrawable}.</dd>
@@ -68,15 +100,16 @@ In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
</dd>
<dt>example:</dt>
-<dd>With an image saved at <code>res/drawable/myimage.png</code>, this layout XML will apply
+
+<dd>With an image saved at <code>res/drawable/myimage.png</code>, this layout XML applies
the image to a View:
<pre>
&lt;ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- <strong>android:src="@drawable/myimage"</strong> />
+ android:src="@drawable/myimage" />
</pre>
-<p>This application code will retrieve the image as a {@link
+<p>The following application code retrieves the image as a {@link
android.graphics.drawable.Drawable}:</p>
<pre>
Resources res = {@link android.content.Context#getResources()};
@@ -97,50 +130,218 @@ Drawable drawable = res.{@link android.content.res.Resources#getDrawable(int) ge
+<h3 id="XmlBitmap">XML Bitmap</h3>
+<p>An XML bitmap is a resource defined in XML that points to a bitmap file. The effect is an alias for a
+raw bitmap file. The XML can specify additional properties for the bitmap such as dithering and tiling.</p>
+<p class="note"><strong>Note:</strong> You can use a {@code &lt;bitmap&gt;} element as a child of
+an {@code &lt;item&gt;} element. For
+example, when creating a <a href="#StateList">state list</a> or <a href="#LayerList">layer list</a>,
+you can exclude the {@code android:drawable}
+attribute from an {@code &lt;item&gt;} element and nest a {@code &lt;bitmap&gt;} inside it
+that defines the drawable item.</p>
-<h2 id="NinePatch">Nine-Patch File</h2>
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.BitmapDrawable}.</dd>
+
+<dt>resource reference:</dt>
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code></li><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;<a href="#bitmap-element">bitmap</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@[package:]drawable/<em>drawable_resource</em>"
+ android:antialias=["true" | "false"]
+ android:dither=["true" | "false"]
+ android:filter=["true" | "false"]
+ android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
+ "fill_vertical" | "center_horizontal" | "fill_horizontal" |
+ "center" | "fill" | "clip_vertical" | "clip_horizontal"]
+ android:tileMode=["disabled" | "clamp" | "repeat" | "mirror"] /&gt;
+</pre>
+</dd>
+
+
+<dt>elements:</dt>
+<dd>
+<dl class="tag-list">
+
+ <dt id="bitmap-element"><code>&lt;bitmap&gt;</code></dt>
+ <dd>Defines the bitmap source and its properties.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>. This is required only if the
+<code>&lt;bitmap&gt;</code> is the root element&mdash;it is not needed when the
+<code>&lt;bitmap&gt;</code> is nested inside an <code>&lt;item&gt;</code>.</dd>
+ <dt><code>android:src</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource.</dd>
+ <dt><code>android:antialias</code></dt>
+ <dd><em>Boolean</em>. Enables or disables antialiasing.</dd>
+ <dt><code>android:dither</code></dt>
+ <dd><em>Boolean</em>. Enables or disables dithering of the bitmap if the bitmap does not
+have the same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with an RGB 565
+screen).</dd>
+ <dt><code>android:filter</code></dt>
+ <dd><em>Boolean</em>. Enables or disables bitmap filtering. Filtering is used when the
+bitmap is shrunk or stretched to smooth its apperance.</dd>
+ <dt><code>android:gravity</code></dt>
+ <dd><em>Keyword</em>. Defines the gravity for the bitmap. The gravity indicates where to
+position the drawable in its container if the bitmap is smaller than the container.
+ <p>Must be one or more (separated by '|') of the following constant values:</p>
+<table>
+<tr><th>Value</th><th>Description</th></tr>
+<tr><td><code>top</code></td>
+<td>Put the object at the top of its container, not changing its size.</td></tr>
+<tr><td><code>bottom</code></td>
+<td>Put the object at the bottom of its container, not changing its size. </td></tr>
+<tr><td><code>left</code></td>
+<td>Put the object at the left edge of its container, not changing its size. </td></tr>
+<tr><td><code>right</code></td>
+<td>Put the object at the right edge of its container, not changing its size. </td></tr>
+<tr><td><code>center_vertical</code></td>
+<td>Place object in the vertical center of its container, not changing its size. </td></tr>
+<tr><td><code>fill_vertical</code></td>
+<td>Grow the vertical size of the object if needed so it completely fills its container. </td></tr>
+<tr><td><code>center_horizontal</code></td>
+<td>Place object in the horizontal center of its container, not changing its size. </td></tr>
+<tr><td><code>fill_horizontal</code></td>
+<td>Grow the horizontal size of the object if needed so it completely fills its container.
+</td></tr>
+<tr><td><code>center</code></td>
+<td>Place the object in the center of its container in both the vertical and horizontal axis, not
+changing its size. </td></tr>
+<tr><td><code>fill</code></td>
+<td>Grow the horizontal and vertical size of the object if needed so it completely fills its
+container. This is the default.</td></tr>
+<tr><td><code>clip_vertical</code></td>
+<td>Additional option that can be set to have the top and/or bottom edges of the child clipped to
+its container's bounds. The clip is based on the vertical gravity: a top gravity clips the
+bottom edge, a bottom gravity clips the top edge, and neither clips both edges.
+</td></tr>
+<tr><td><code>clip_horizontal</code></td>
+<td>Additional option that can be set to have the left and/or right edges of the child clipped to
+its container's bounds. The clip is based on the horizontal gravity: a left gravity clips
+the right edge, a right gravity clips the left edge, and neither clips both edges.
+</td></tr>
+</table>
+ </dd>
+ <dt><code>android:tileMode</code></dt>
+ <dd><em>Keyword</em>. Defines the tile mode. When the tile mode is enabled, the bitmap is
+repeated. Gravity is ignored when the tile mode is enabled.
+ <p>Must be one of the following constant values:</p>
+<table>
+<tr><th>Value</th><th>Description</th></tr>
+<tr><td><code>disabled</code></td>
+<td>Do not tile the bitmap. This is the default value.</td></tr>
+<tr><td><code>clamp</code></td>
+<td>Replicates the edge color if the shader draws outside of its original bounds</td></tr>
+<tr><td><code>repeat</code></td>
+<td>Repeats the shader's image horizontally and vertically.</td></tr>
+<tr><td><code>mirror</code></td>
+<td>Repeats the shader's image horizontally and vertically, alternating mirror images so that
+adjacent images always seam.</td></tr>
+</table>
+
+ </dd>
+ </dl>
+ </dd>
+
+</dl>
+</dd> <!-- end elements and attributes -->
+
+<dt>example:</dt>
+<dd>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/icon"
+ android:tileMode="repeat" /&gt;
+</pre>
+
+</dd>
+
+<dt>see also:</dt>
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.BitmapDrawable}</li>
+ <li><a href="{@docRoot}guide/topics/resources/providing-resources.html#AliasResources">Creating
+alias resources</a>
+</ul>
+</dd>
+
+</dl>
+
+
+
+
+
+
+<h2 id="NinePatch">Nine-Patch</h2>
<p>A {@link android.graphics.NinePatch} is a PNG image in which you can define stretchable regions
-that Android will scale when content within the View exceeds the normal image bounds. You will
+that Android scales when content within the View exceeds the normal image bounds. You
typically assign this type of image as the background of a View that has at least one dimension set
to {@code "wrap_content"}, and when the View grows to accomodate the content, the Nine-Patch image
-will also be scaled to match the size of the View. An example use of a Nine-Patch image is the
+is also scaled to match the size of the View. An example use of a Nine-Patch image is the
background used by Android's standard {@link android.widget.Button} widget, which must stretch to
accommodate the text (or image) inside the button.</p>
-<p>For a complete discussion about how to define a Nine-Patch file with stretchable regions,
+<p>Same as with a normal <a href="#Bitmap">bitmap</a>, you can reference a Nine-Patch file directly
+or from a resource defined by XML.</p>
+
+<p>For a complete discussion about how to create a Nine-Patch file with stretchable regions,
see the <a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">2D Graphics</a>
document.</p>
+
+<h3 id="NinePatchFile">Nine-Patch File</h3>
+
<dl class="xml">
<dt>file location:</dt>
<dd><code>res/drawable/<em>filename</em>.9.png</code><br/>
-The filename will be used as the resource ID.</dd>
+The filename is used as the resource ID.</dd>
<dt>compiled resource datatype:</dt>
<dd>Resource pointer to a {@link android.graphics.drawable.NinePatchDrawable}.</dd>
<dt>resource reference:</dt>
+
<dd>
In Java: <code>R.drawable.<em>filename</em></code><br/>
In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
</dd>
<dt>example:</dt>
-<dd>With an image saved at <code>res/drawable/myninepatch.9.png</code>, this layout XML will
-apply the Nine-Patch to a View:
+
+<dd>With an image saved at <code>res/drawable/myninepatch.9.png</code>, this layout XML
+applies the Nine-Patch to a View:
<pre>
&lt;Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- <strong>android:background="@drawable/myninepatch"</strong> />
+ android:background="@drawable/myninepatch" />
</pre>
</dd>
<dt>see also:</dt>
+
<dd>
<ul>
<li><a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">2D Graphics</a></li>
@@ -153,6 +354,238 @@ apply the Nine-Patch to a View:
+<h3 id="NinePatchXml">XML Nine-Patch</h3>
+
+<p>An XML Nine-Patch is a resource defined in XML that points to a Nine-Patch file. The XML can
+specify dithering for the image.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.NinePatchDrawable}.</dd>
+
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;<a href="#bitmap-element">nine-patch</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@[package:]drawable/<em>drawable_resource</em>"
+ android:dither=["true" | "false"] /&gt;
+</pre>
+</dd>
+
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="layerlist-element"><code>&lt;bitmap&gt;</code></dt>
+ <dd>Defines the bitmap source and its properties.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ <dt><code>android:src</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a Nine-Patch
+file.</dd>
+ <dt><code>android:dither</code></dt>
+ <dd><em>Boolean</em>. Enables or disables dithering of the bitmap if the bitmap does not
+have the same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with an RGB 565
+screen).</dd>
+ </dl>
+ </dd>
+</dl>
+</dd>
+
+
+<dt>example:</dt>
+
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/myninepatch"
+ android:dither="false" /&gt;
+</pre>
+</dd>
+</dl>
+
+
+
+
+
+
+<h2 id="LayerList">Layer List</h2>
+
+<p>A {@link android.graphics.drawable.LayerDrawable} is a drawable object
+that manages an array of other drawables. Each drawable in the list is drawn in the order of the
+list&mdash;the last drawable in the list is drawn on top.</p>
+
+<p>Each drawable is represented by an {@code &lt;item&gt;} element inside a single {@code
+&lt;layer-list&gt;} element.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.LayerDrawable}.</dd>
+
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;<a href="#layerlist-element">layer-list</a>
+ xmlns:android="http://schemas.android.com/apk/res/android" &gt;
+ &lt;<a href="#layerlist-item-element">item</a>
+ android:drawable="@[package:]drawable/<em>drawable_resource</em>"
+ android:id="@[+][<em>package</em>:]id/<i>resource_name</i>"
+ android:top="<em>dimension</em>"
+ android:right="<em>dimension</em>"
+ android:bottom="<em>dimension</em>"
+ android:left="<em>dimension</em>" /&gt;
+&lt;/selector>
+</pre>
+</dd>
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="layerlist-element"><code>&lt;layer-list&gt;</code></dt>
+ <dd><strong>Required.</strong> This must be the root element. Contains one or more {@code
+&lt;item>} elements.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ </dl>
+ </dd>
+ <dt id="layerlist-item-element"><code>&lt;item&gt;</code></dt>
+ <dd>Defines a drawable to place in the layer drawable, in a position defined by its attributes.
+Must be a child of a <code>&lt;selector&gt;</code> element. Accepts child {@code &lt;bitmap&gt;}
+elements.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource.</dd>
+ <dt><code>android:id</code></dt>
+ <dd><em>Resource ID</em>. A unique resource ID for this drawable. To create a new resource
+ID for this item, use the form:
+<code>"@+id/<em>name</em>"</code>. The plus symbol indicates that this should be created as a new
+ID. You can use this identifier to
+retrieve and modify the drawable with {@link android.view.View#findViewById(int)
+View.findViewById()} or {@link android.app.Activity#findViewById(int) Activity.findViewById()}.</dd>
+ <dt><code>android:top</code></dt>
+ <dd><em>Integer</em>. The top offset in pixels.</dd>
+ <dt><code>android:right</code></dt>
+ <dd><em>Integer</em>. The right offset in pixels.</dd>
+ <dt><code>android:bottom</code></dt>
+ <dd><em>Integer</em>. The bottom offset in pixels.</dd>
+ <dt><code>android:left</code></dt>
+ <dd><em>Integer</em>. The left offset in pixels.</dd>
+ </dl>
+ <p>All drawable items are scaled to fit the size of the containing View, by default. Thus,
+placing your images in a layer list at different positions might increase the size of the View and
+some images scale as appropriate. To avoid
+scaling items in the list, use a {@code &lt;bitmap&gt;} element inside the {@code
+&lt;item&gt;} element to specify the drawable and define the gravity to something that does not
+scale, such as {@code "center"}. For example, the following {@code &lt;item&gt;} defines an item
+that scales to fit its container View:</p>
+<pre>
+&lt;item android:drawable="@drawable/image" /&gt;
+</pre>
+
+<p>To avoid scaling, the following example uses a {@code &lt;bitmap&gt;} element with centered
+gravity:</p>
+<pre>
+&lt;item&gt;
+ &lt;bitmap android:src="<b>@drawable/image</b>"
+ android:gravity="center" /&gt;
+&lt;/item&gt;
+</pre>
+ </dd>
+
+</dl>
+</dd> <!-- end elements and attributes -->
+
+<dt>example:</dt>
+
+<dd>XML file saved at <code>res/drawable/layers.xml</code>:
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;layer-list xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;item&gt;
+ &lt;bitmap android:src="@drawable/android_red"
+ android:gravity="center" /&gt;
+ &lt;/item&gt;
+ &lt;item android:top="10dp" android:left="10dp"&gt;
+ &lt;bitmap android:src="@drawable/android_green"
+ android:gravity="center" /&gt;
+ &lt;/item&gt;
+ &lt;item android:top="20dp" android:left="20dp"&gt;
+ &lt;bitmap android:src="@drawable/android_blue"
+ android:gravity="center" /&gt;
+ &lt;/item&gt;
+&lt;/layer-list&gt;
+</pre>
+<p>Notice that this example uses a nested {@code &lt;bitmap&gt;} element to define the drawable
+resource for each item with a "center" gravity. This ensures that none of the images are scaled to
+fit the size of the container, due to resizing caused by the offset images.</p>
+
+<p>This layout XML applies the drawable to a View:</p>
+<pre>
+&lt;ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:src="@drawable/layers" /&gt;
+</pre>
+
+<p>The result is a stack of increasingly offset images:</p>
+<img src="{@docRoot}images/resources/layers.png" alt="" />
+</dd> <!-- end example -->
+
+<dt>see also:</dt>
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.LayerDrawable}</li>
+</ul>
+</dd>
+
+</dl>
+
+
+
+
@@ -163,33 +596,36 @@ apply the Nine-Patch to a View:
that uses a several different images to represent the same graphic, depending on the state of
the object. For example, a {@link
android.widget.Button} widget can exist in one of several different states (pressed, focused,
-or niether) and, using a state list drawable, you can provide a different button image for each
+or niether) and, using a state list drawable, you can provide a different background image for each
state.</p>
<p>You can describe the state list in an XML file. Each graphic is represented by an {@code
&lt;item>} element inside a single {@code &lt;selector>} element. Each {@code &lt;item>}
uses various attributes to describe the state in which it should be used as the graphic for the
drawable.</p>
+
<p>During each state change, the state list is traversed top to bottom and the first item that
-matches the current state will be used&mdash;the selection is <em>not</em> based on the "best
+matches the current state is used&mdash;the selection is <em>not</em> based on the "best
match," but simply the first item that meets the minimum criteria of the state.</p>
<dl class="xml">
<dt>file location:</dt>
<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
-The filename will be used as the resource ID.</dd>
+The filename is used as the resource ID.</dd>
<dt>compiled resource datatype:</dt>
<dd>Resource pointer to a {@link android.graphics.drawable.StateListDrawable}.</dd>
<dt>resource reference:</dt>
+
<dd>
In Java: <code>R.drawable.<em>filename</em></code><br/>
In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
</dd>
<dt>syntax:</dt>
+
<dd>
<pre class="stx">
&lt;?xml version="1.0" encoding="utf-8"?>
@@ -202,7 +638,6 @@ In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
android:state_pressed=["true" | "false"]
android:state_focused=["true" | "false"]
android:state_selected=["true" | "false"]
- android:state_active=["true" | "false"]
android:state_checkable=["true" | "false"]
android:state_checked=["true" | "false"]
android:state_enabled=["true" | "false"]
@@ -212,6 +647,7 @@ In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
</dd>
<dt>elements:</dt>
+
<dd>
<dl class="tag-list">
@@ -224,8 +660,8 @@ In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
<dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
<code>"http://schemas.android.com/apk/res/android"</code>.
<dt><code>android:constantSize</code></dt>
- <dd><em>Boolean</em>. "true" if the drawable's reported internal size will remain constant as the state
-changes (the size will be the maximum of all of the states); "false" if the size will vary based on
+ <dd><em>Boolean</em>. "true" if the drawable's reported internal size remains constant as the state
+changes (the size is the maximum of all of the states); "false" if the size varies based on
the current state. Default is false.</dd>
<dt><code>android:dither</code></dt>
<dd><em>Boolean</em>. "true" to enable dithering of the bitmap if the bitmap does not have the same pixel
@@ -270,9 +706,9 @@ receiving touch/click events); "false" if it should be used when the object is d
application is in the foreground), "false" if this item should be used when the application
window does not have focus (for example, if the notification shade is pulled down or a dialog appears).</dd>
</dl>
- <p class="note"><strong>Note:</strong>Remember that the first item in the state list that
-matches the current state of the object will be applied. So if the first item in the list contains
-none of the state attributes above, then it will be applied every time, which is why your
+ <p class="note"><strong>Note:</strong> Remember that Android applies the first item in the state list that
+matches the current state of the object. So, if the first item in the list contains
+none of the state attributes above, then it is applied every time, which is why your
default value should always be last (as demonstrated in the following example).</p>
</dd>
@@ -280,6 +716,7 @@ default value should always be last (as demonstrated in the following example).<
</dd> <!-- end elements and attributes -->
<dt>example:</dt>
+
<dd>XML file saved at <code>res/drawable/button.xml</code>:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
@@ -292,12 +729,12 @@ default value should always be last (as demonstrated in the following example).<
&lt;/selector>
</pre>
-<p>This layout XML will apply the drawable to a View:</p>
+<p>This layout XML applies the drawable to a View:</p>
<pre>
&lt;ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- <strong>android:src="@drawable/button"</strong> />
+ android:src="@drawable/button" />
</pre>
</dd> <!-- end example -->
@@ -317,106 +754,513 @@ default value should always be last (as demonstrated in the following example).<
+<h2 id="LevelList">Level List</h2>
+<p>A Drawable that manages a number of alternate Drawables, each assigned a maximum numerical
+value. Setting the level value of the drawable with {@link
+android.graphics.drawable.Drawable#setLevel(int) setLevel()} loads the drawable resource in the
+level list that has a {@code android:maxLevel} value greater than or equal to the value
+passed to the method.</p>
+<dl class="xml">
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.LevelListDrawable}.</dd>
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;<a href="#levellist-element">level-list</a>
+ xmlns:android="http://schemas.android.com/apk/res/android" &gt;
+ &lt;<a href="#levellist-item-element">item</a>
+ android:drawable="@drawable/<i>drawable_resource</i>"
+ android:maxLevel="<i>integer</i>"
+ android:minLevel="<i>integer</i>" /&gt;
+&lt;/level-list&gt;
+</pre>
+</dd>
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="levellist-element"><code>&lt;level-list&gt;</code></dt>
+ <dd>This must be the root element. Contains one or more {@code &lt;item&gt;} elements.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ </dl>
+ </dd>
+
+ <dt id="levellist-item-element"><code>&lt;item&gt;</code></dt>
+ <dd>Defines a drawable to use at a certain level.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource to be inset.</dd>
+ <dt><code>android:maxLevel</code></dt>
+ <dd><em>Integer</em>. The maximum level allowed for this item.</dd>
+ <dt><code>android:minLevel</code></dt>
+ <dd><em>Integer</em>. The minimum level allowed for this item.</dd>
+ </dl>
+ </dd>
+</dl>
+
+</dd>
+
+<dt>example:</dt>
+
+<dd>
+
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;level-list xmlns:android="http://schemas.android.com/apk/res/android" &gt;
+ &lt;item
+ android:drawable="@drawable/status_off"
+ android:maxLevel="0" /&gt;
+ &lt;item
+ android:drawable="@drawable/status_on"
+ android:maxLevel="1" /&gt;
+&lt;/level-list&gt;
+</pre>
+<p>Once this is applied to a {@link android.view.View}, the level can be changed with {@link
+android.graphics.drawable.Drawable#setLevel(int) setLevel()} or {@link
+android.widget.ImageView#setImageLevel(int) setImageLevel()}.</p>
+
+</dd>
+
+<dt>see also:</dt>
+
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.LevelListDrawable}</li>
+</ul>
+</dd>
+
+</dl>
-<h2 id="Color">Color</h2>
-<p>This is a color defined in XML that's used as a drawable to fill a rectangular space,
-with optionally rounded corners. This kind of drawable behaves like a color fill.</p>
-<p class="note"><strong>Note:</strong> A color drawable is a simple resource that is referenced
-using the value provided in the {@code name} attribute (not the name of the XML file). As
-such, you can combine a color drawable resources with other simple resources in the one XML file,
-under one {@code &lt;resources>} element.</p>
+
+<h2 id="Transition">Transition Drawable</h2>
+
+<p>A {@link android.graphics.drawable.TransitionDrawable} is a drawable object
+that can cross-fade between the two drawable resources.</p>
+
+<p>Each drawable is represented by an {@code &lt;item&gt;} element inside a single {@code
+&lt;transition&gt;} element. No more than two items are supported. To transition forward, call
+{@link android.graphics.drawable.TransitionDrawable#startTransition(int) startTransition()}. To
+transition backward, call {@link android.graphics.drawable.TransitionDrawable#reverseTransition(int)
+reverseTransition()}.</p>
+
<dl class="xml">
<dt>file location:</dt>
-<dd><code>res/drawable/<em>filename</em>.png</code><br/>
-The filename is arbitrary. The element's {@code name} will be used as the resource ID.</dd>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
<dt>compiled resource datatype:</dt>
-<dd>Resource pointer to a {@link android.graphics.drawable.PaintDrawable}.</dd>
+<dd>Resource pointer to a {@link android.graphics.drawable.TransitionDrawable}.</dd>
<dt>resource reference:</dt>
+
<dd>
-In Java: <code>R.drawable.<em>color_name</em></code><br/>
-In XML: <code>@[<em>package</em>:]drawable/<em>color_name</em></code>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
</dd>
<dt>syntax:</dt>
+
<dd>
<pre class="stx">
-&lt;?xml version="1.0" encoding="utf-8"?&gt;
-&lt;<a href="#color-resources-element">resources</a>>
- &lt;<a href="#drawable-element">drawable</a>
- name="<em>color_name</em>"
- &gt;<em>color</em>&lt;/drawable&gt;
-&lt;/resources&gt;
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;<a href="#transition-element">layer-list</a>
+xmlns:android="http://schemas.android.com/apk/res/android" &gt;
+ &lt;<a href="#transition-item-element">item</a>
+ android:drawable="@[package:]drawable/<em>drawable_resource</em>"
+ android:id="@[+][<em>package</em>:]id/<i>resource_name</i>"
+ android:top="<em>dimension</em>"
+ android:right="<em>dimension</em>"
+ android:bottom="<em>dimension</em>"
+ android:left="<em>dimension</em>" /&gt;
+&lt;/selector>
</pre>
</dd>
<dt>elements:</dt>
+
<dd>
<dl class="tag-list">
- <dt id="color-resources-element"><code>&lt;resources&gt;</code></dt>
- <dd><strong>Required.</strong> This must be the root node.
- <p>No attributes.</p>
+ <dt id="transition-element"><code>&lt;transition&gt;</code></dt>
+ <dd><strong>Required.</strong> This must be the root element. Contains one or more {@code
+&lt;item>} elements.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ </dl>
</dd>
- <dt id="drawable-element"><code>&lt;drawable&gt;</code></dt>
- <dd>A color to use as a drawable rectangle. The value can be
- any valid hexadecimal color value or a <a href="more-resources.html#Color">color
- resource</a>. A color value always begins with a pound (#) character, followed
- by the Alpha-Red-Green-Blue information in one of the following formats:
- #<em>RGB</em>, #<em>RRGGBB</em>, #<em>ARGB</em>, or #<em>AARRGGBB</em>.
+ <dt id="transition-item-element"><code>&lt;item&gt;</code></dt>
+ <dd>Defines a drawable to place in the layer drawable, in a position defined by its attributes.
+Must be a child of a <code>&lt;selector&gt;</code> element. Accepts child {@code &lt;bitmap&gt;}
+elements.
<p class="caps">attributes:</p>
<dl class="atn-list">
- <dt><code>name</code></dt>
- <dd><em>String</em>. <strong>Required</strong>.
- A name for the color. This name will be used as the resource ID.</dd>
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource.</dd>
+ <dt><code>android:id</code></dt>
+ <dd><em>Resource ID</em>. A unique resource ID for this drawable. To create a new resource
+ID for this item, use the form:
+<code>"@+id/<em>name</em>"</code>. The plus symbol indicates that this should be created as a new
+ID. You can use this identifier to
+retrieve and modify the drawable with {@link android.view.View#findViewById(int)
+View.findViewById()} or {@link android.app.Activity#findViewById(int) Activity.findViewById()}.</dd>
+ <dt><code>android:top</code></dt>
+ <dd><em>Integer</em>. The top offset in pixels.</dd>
+ <dt><code>android:right</code></dt>
+ <dd><em>Integer</em>. The right offset in pixels.</dd>
+ <dt><code>android:bottom</code></dt>
+ <dd><em>Integer</em>. The bottom offset in pixels.</dd>
+ <dt><code>android:left</code></dt>
+ <dd><em>Integer</em>. The left offset in pixels.</dd>
</dl>
-
</dd>
</dl>
</dd> <!-- end elements and attributes -->
<dt>example:</dt>
-<dd>XML file saved at <code>res/drawable/colors.xml</code>:
+
+<dd>XML file saved at <code>res/drawable/transition.xml</code>:
<pre>
-&lt;?xml version="1.0" encoding="utf-8"?>
-&lt;resources>
- &lt;drawable name="solid_red">#f00&lt;/drawable>
- &lt;drawable name="solid_blue">#0000ff&lt;/drawable>
-&lt;/resources>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;transition xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;item android:drawable="@drawable/on" /&gt;
+ &lt;item android:drawable="@drawable/off" /&gt;
+&lt;/layer-list&gt;
</pre>
- <p>This layout XML will apply a color drawable to a View:</p>
+
+<p>This layout XML applies the drawable to a View:</p>
<pre>
-&lt;TextView
- android:layout_width="fill_parent"
+&lt;ImageButton
+ android:id="@+id/button"
android:layout_height="wrap_content"
- <strong>android:background="@drawable/solid_blue"</strong> />
+ android:layout_width="wrap_content"
+ android:src="@drawable/transition" /&gt;
+</pre>
+
+<p>And the following code performs a 500ms transition from the first item to the second:</p>
+<pre>
+ImageButton button = (ImageButton) findViewById(R.id.button);
+TransitionDrawable drawable = (TransitionDrawable) button.getDrawable();
+drawable.startTransition(500);
+</pre>
+
+</dd> <!-- end example -->
+
+<dt>see also:</dt>
+
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.TransitionDrawable}</li>
+</ul>
+</dd>
+
+</dl>
+
+
+
+
+
+
+
+
+<h2 id="Inset">Inset Drawable</h2>
+
+<p>A drawable defined in XML that insets another drawable by a specified distance. This is used when
+a View needs a background that is smaller than the View's actual bounds.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.InsetDrawable}.</dd>
+
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;<a href="#inset-element">inset</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/<i>drawable_resource</i>"
+ android:insetTop="<i>dimension</i>"
+ android:insetRight="<i>dimension</i>"
+ android:insetBottom="<i>dimension</i>"
+ android:insetLeft="<i>dimension</i>" /&gt;
</pre>
- <p>This application code will get a color drawable and apply it to a View:</p>
+</dd>
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="inset-element"><code>&lt;inset&gt;</code></dt>
+ <dd>Defines the inset drawable. This must be the root element.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource to be inset.</dd>
+ <dt><code>android:insetTop</code></dt>
+ <dd><em>Dimension</em>. The top inset, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a></dd>
+ <dt><code>android:insetRight</code></dt>
+ <dd><em>Dimension</em>. The right inset, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a></dd>
+ <dt><code>android:insetBottom</code></dt>
+ <dd><em>Dimension</em>. The bottom inset, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a></dd>
+ <dt><code>android:insetLeft</code></dt>
+ <dd><em>Dimension</em>. The left inset, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a></dd>
+ </dl>
+ </dd>
+</dl>
+
+</dd>
+
+<dt>example:</dt>
+
+<dd>
<pre>
-Resources res = {@link android.content.Context#getResources()};
-Drawable redDrawable = res.{@link android.content.res.Resources#getDrawable(int) getDrawable}(R.drawable.solid_red);
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/background"
+ android:insetTop="10dp"
+ android:insetLeft="10dp" /&gt;
+</pre>
+</dd>
+
+<dt>see also:</dt>
+
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.InsetDrawable}</li>
+</ul>
+</dd>
+
+</dl>
+
+
+
+
+
+
+
+
+<h2 id="Clip">Clip Drawable</h2>
+
+<p>A drawable defined in XML that clips another drawable based on this Drawable's current level. You
+can control how much the child drawable gets clipped in width and height based on the level, as well
+as a gravity to control where it is placed in its overall container. Most often used to implement
+things like progress bars.</p>
+
+<dl class="xml">
-TextView tv = (TextView) findViewByID(R.id.text);
-tv.setBackground(redDrawable);
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.ClipDrawable}.</dd>
+
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;<a href="#clip-element">clip</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/<i>drawable_resource</i>"
+ android:clipOrientation=["horizontal" | "vertical"]
+ android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
+ "fill_vertical" | "center_horizontal" | "fill_horizontal" |
+ "center" | "fill" | "clip_vertical" | "clip_horizontal"] /&gt;
+</pre>
+</dd>
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="clip-element"><code>&lt;clip&gt;</code></dt>
+ <dd>Defines the clip drawable. This must be the root element.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource to be clipped.</dd>
+ <dt><code>android:clipOrientation</code></dt>
+ <dd><em>Keyword</em>. The orientation for the clip.
+ <p>Must be one of the following constant values:</p>
+<table>
+<tr><th>Value</th><th>Description</th></tr>
+<tr><td><code>horizontal</code></td>
+<td>Clip the drawable horizontally.</td></tr>
+<tr><td><code>vertical</code></td>
+<td>Clip the drawable vertically.</td></tr>
+</table>
+ </dd>
+ <dt><code>android:gravity</code></dt>
+ <dd><em>Keyword</em>. Specifies where to clip within the drawable.
+ <p>Must be one or more (separated by '|') of the following constant values:</p>
+<table>
+<tr><th>Value</th><th>Description</th></tr>
+<tr><td><code>top</code></td>
+<td>Put the object at the top of its container, not changing its size. When {@code
+clipOrientation} is {@code "vertical"}, clipping occurs at the bottom of the drawable.</td></tr>
+<tr><td><code>bottom</code></td>
+<td>Put the object at the bottom of its container, not changing its size. When {@code
+clipOrientation} is {@code "vertical"}, clipping occurs at the top of the drawable.</td></tr>
+<tr><td><code>left</code></td>
+<td>Put the object at the left edge of its container, not changing its size. This is the
+default. When {@code clipOrientation} is {@code "horizontal"}, clipping occurs at the right side of
+the drawable. This is the default.</td></tr>
+<tr><td><code>right</code></td>
+<td>Put the object at the right edge of its container, not changing its size. When {@code
+clipOrientation} is {@code "horizontal"}, clipping occurs at the left side of
+the drawable.</td></tr>
+<tr><td><code>center_vertical</code></td>
+<td>Place object in the vertical center of its container, not changing its size. Clipping behaves
+the same as when gravity is {@code "center"}.</td></tr>
+<tr><td><code>fill_vertical</code></td>
+<td>Grow the vertical size of the object if needed so it completely fills its container. When {@code
+clipOrientation} is {@code "vertical"}, no clipping occurs because the drawable fills the
+vertical space (unless the drawable level is 0, in which case it's not visible).</td></tr>
+<tr><td><code>center_horizontal</code></td>
+<td>Place object in the horizontal center of its container, not changing its size.
+Clipping behaves the same as when gravity is {@code "center"}.</td></tr>
+<tr><td><code>fill_horizontal</code></td>
+<td>Grow the horizontal size of the object if needed so it completely fills its container. When
+{@code clipOrientation} is {@code "horizontal"}, no clipping occurs because the drawable fills the
+horizontal space (unless the drawable level is 0, in which case it's not visible).
+</td></tr>
+<tr><td><code>center</code></td>
+<td>Place the object in the center of its container in both the vertical and horizontal axis, not
+changing its size. When {@code
+clipOrientation} is {@code "horizontal"}, clipping occurs on the left and right. When {@code
+clipOrientation} is {@code "vertical"}, clipping occurs on the top and bottom.</td></tr>
+<tr><td><code>fill</code></td>
+<td>Grow the horizontal and vertical size of the object if needed so it completely fills its
+container. No clipping occurs because the drawable fills the
+horizontal and vertical space (unless the drawable level is 0, in which case it's not
+visible).</td></tr>
+<tr><td><code>clip_vertical</code></td>
+<td>Additional option that can be set to have the top and/or bottom edges of the child clipped to
+its container's bounds. The clip is based on the vertical gravity: a top gravity clips the
+bottom edge, a bottom gravity clips the top edge, and neither clips both edges.
+</td></tr>
+<tr><td><code>clip_horizontal</code></td>
+<td>Additional option that can be set to have the left and/or right edges of the child clipped to
+its container's bounds. The clip is based on the horizontal gravity: a left gravity clips
+the right edge, a right gravity clips the left edge, and neither clips both edges.
+</td></tr>
+</table></dd>
+ </dl>
+ </dd>
+</dl>
+
+</dd> <!-- end elements and attributes -->
+
+<dt>example:</dt>
+
+<dd>XML file saved at <code>res/drawable/clip.xml</code>:
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/android"
+ android:clipOrientation="horizontal"
+ android:gravity="left" /&gt;
+&lt;/shape>
+</pre>
+ <p>The following layout XML applies the clip drawable to a View:</p>
+<pre>
+&lt;ImageView
+ android:id="@+id/image"
+ android:background="@drawable/clip"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+</pre>
+
+ <p>The following code gets the drawable and increases the amount of clipping in order to
+progressively reveal the image:</p>
+<pre>
+ImageView imageview = (ImageView) findViewById(R.id.image);
+ClipDrawable drawable = (ClipDrawable) imageview.getDrawable();
+drawable.setLevel(drawable.getLevel() + 1000);
</pre>
+
+<p>Increasing the level reduces the amount of clipping and slowly reveals the image. Here it is
+at a level of 7000:</p>
+<img src="{@docRoot}images/resources/clip.png" alt="" />
+
+<p class="note"><strong>Note:</strong> The default level is 0, which is fully clipped so the image
+is not visible. When the level is 10,000, the image is not clipped and completely visible.</p>
</dd> <!-- end example -->
<dt>see also:</dt>
+
<dd>
<ul>
- <li>{@link android.graphics.drawable.PaintDrawable}</li>
+ <li>{@link android.graphics.drawable.ClipDrawable}</li>
</ul>
</dd>
@@ -430,10 +1274,139 @@ tv.setBackground(redDrawable);
+<h2 id="Scale">Scale Drawable</h2>
+
+<p>A drawable defined in XML that changes the size of another drawable based on its current
+level.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
+The filename is used as the resource ID.</dd>
+
+<dt>compiled resource datatype:</dt>
+<dd>Resource pointer to a {@link android.graphics.drawable.ScaleDrawable}.</dd>
+
+<dt>resource reference:</dt>
+
+<dd>
+In Java: <code>R.drawable.<em>filename</em></code><br/>
+In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
+</dd>
+
+<dt>syntax:</dt>
+
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;<a href="#scale-element">scale</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/<i>drawable_resource</i>"
+ android:scaleGravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
+ "fill_vertical" | "center_horizontal" | "fill_horizontal" |
+ "center" | "fill" | "clip_vertical" | "clip_horizontal"]
+ android:scaleHeight="<i>percentage</i>"
+ android:scaleWidth="<i>percentage</i>" /&gt;
+</pre>
+</dd>
+
+<dt>elements:</dt>
+
+<dd>
+<dl class="tag-list">
+
+ <dt id="scale-element"><code>&lt;scale&gt;</code></dt>
+ <dd>Defines the scale drawable. This must be the root element.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
+ <dt><code>android:drawable</code></dt>
+ <dd><em>Drawable resource</em>. <strong>Required</strong>. Reference to a drawable
+resource.</dd>
+ <dt><code>android:scaleGravity</code></dt>
+ <dd><em>Keyword</em>. Specifies the gravity position after scaling.
+ <p>Must be one or more (separated by '|') of the following constant values:</p>
+<table>
+<tr><th>Value</th><th>Description</th></tr>
+<tr><td><code>top</code></td>
+<td>Put the object at the top of its container, not changing its size.</td></tr>
+<tr><td><code>bottom</code></td>
+<td>Put the object at the bottom of its container, not changing its size. </td></tr>
+<tr><td><code>left</code></td>
+<td>Put the object at the left edge of its container, not changing its size. This is the
+default.</td></tr>
+<tr><td><code>right</code></td>
+<td>Put the object at the right edge of its container, not changing its size. </td></tr>
+<tr><td><code>center_vertical</code></td>
+<td>Place object in the vertical center of its container, not changing its size. </td></tr>
+<tr><td><code>fill_vertical</code></td>
+<td>Grow the vertical size of the object if needed so it completely fills its container. </td></tr>
+<tr><td><code>center_horizontal</code></td>
+<td>Place object in the horizontal center of its container, not changing its size. </td></tr>
+<tr><td><code>fill_horizontal</code></td>
+<td>Grow the horizontal size of the object if needed so it completely fills its container.
+</td></tr>
+<tr><td><code>center</code></td>
+<td>Place the object in the center of its container in both the vertical and horizontal axis, not
+changing its size. </td></tr>
+<tr><td><code>fill</code></td>
+<td>Grow the horizontal and vertical size of the object if needed so it completely fills its
+container. </td></tr>
+<tr><td><code>clip_vertical</code></td>
+<td>Additional option that can be set to have the top and/or bottom edges of the child clipped to
+its container's bounds. The clip is based on the vertical gravity: a top gravity clips the
+bottom edge, a bottom gravity clips the top edge, and neither clips both edges.
+</td></tr>
+<tr><td><code>clip_horizontal</code></td>
+<td>Additional option that can be set to have the left and/or right edges of the child clipped to
+its container's bounds. The clip is based on the horizontal gravity: a left gravity clips
+the right edge, a right gravity clips the left edge, and neither clips both edges.
+</td></tr>
+</table></dd>
+ <dt><code>android:scaleHeight</code></dt>
+ <dd><em>Percentage</em>. The scale height, expressed as a percentage of the drawable's
+bound. The value's format is XX%. For instance: 100%, 12.5%, etc.</dd>
+ <dt><code>android:scaleWidth</code></dt>
+ <dd><em>Percentage</em>. The scale width, expressed as a percentage of the drawable's
+bound. The value's format is XX%. For instance: 100%, 12.5%, etc.</dd>
+ </dl>
+ </dd>
+</dl>
+
+</dd>
+
+<dt>example:</dt>
+
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;scale xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/logo"
+ android:scaleGravity="center_vertical|center_horizontal"
+ android:scaleHeight="80%"
+ android:scaleWidth="80%" /&gt;
+</pre>
+</dd>
+
+<dt>see also:</dt>
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.ScaleDrawable}</li>
+</ul>
+</dd>
+
+</dl>
+
+
+
+
-<h2 id="Shape">Shape</h2>
+<h2 id="Shape">Shape Drawable</h2>
<p>This is a generic shape defined in XML.</p>
@@ -441,23 +1414,32 @@ tv.setBackground(redDrawable);
<dt>file location:</dt>
<dd><code>res/drawable/<em>filename</em>.xml</code><br/>
-The filename will be used as the resource ID.</dd>
+The filename is used as the resource ID.</dd>
<dt>compiled resource datatype:</dt>
<dd>Resource pointer to a {@link android.graphics.drawable.ShapeDrawable}.</dd>
<dt>resource reference:</dt>
+
<dd>
In Java: <code>R.drawable.<em>filename</em></code><br/>
In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
</dd>
<dt>syntax:</dt>
+
<dd>
<pre class="stx">
&lt;?xml version="1.0" encoding="utf-8"?>
-&lt;<a href="#shape-element">shape</a> xmlns:android="http://schemas.android.com/apk/res/android"
+&lt;<a href="#shape-element">shape</a>
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:shape=["rectangle" | "oval" | "line" | "ring"] >
+ &lt;<a href="#corners-element">corners</a>
+ android:radius="<em>integer</em>"
+ android:topLeftRadius="<em>integer</em>"
+ android:topRightRadius="<em>integer</em>"
+ android:bottomLeftRadius="<em>integer</em>"
+ android:bottomRightRadius="<em>integer</em>" /&gt;
&lt;<a href="#gradient-element">gradient</a>
android:angle="<em>integer</em>"
android:centerX="<em>integer</em>"
@@ -467,37 +1449,40 @@ In XML: <code>@[<em>package</em>:]drawable/<em>filename</em></code>
android:gradientRadius="<em>integer</em>"
android:startColor="<em>color</em>"
android:type=["linear" | "radial" | "sweep"]
- android:usesLevel=["true" | "false"] />
+ android:usesLevel=["true" | "false"] /&gt;
+ &lt;<a href="#padding-element">padding</a>
+ android:left="<em>integer</em>"
+ android:top="<em>integer</em>"
+ android:right="<em>integer</em>"
+ android:bottom="<em>integer</em>" /&gt;
+ &lt;<a href="#size-element">size</a>
+ android:width="<em>integer</em>"
+ android:color="<em>color</em>"
+ android:dashWidth="<em>integer</em>"
+ android:dashGap="<em>integer</em>" /&gt;
&lt;<a href="#solid-element">solid</a>
- android:color="<em>color</em>" />
+ android:color="<em>color</em>" /&gt;
&lt;<a href="#stroke-element">stroke</a>
android:width="<em>integer</em>"
android:color="<em>color</em>"
android:dashWidth="<em>integer</em>"
- android:dashGap="<em>integer</em>" />
- &lt;<a href="#padding-element">padding</a>
- android:left="<em>integer</em>"
- android:top="<em>integer</em>"
- android:right="<em>integer</em>"
- android:bottom="<em>integer</em>" />
- &lt;<a href="#corners-element">corners</a>
- android:radius="<em>integer</em>"
- android:topLeftRadius="<em>integer</em>"
- android:topRightRadius="<em>integer</em>"
- android:bottomLeftRadius="<em>integer</em>"
- android:bottomRightRadius="<em>integer</em>" />
+ android:dashGap="<em>integer</em>" /&gt;
&lt;/shape>
</pre>
</dd>
<dt>elements:</dt>
+
<dd>
<dl class="tag-list">
<dt id="shape-element"><code>&lt;shape&gt;</code></dt>
- <dd><strong>Required.</strong> This must be the root element.
+ <dd>The shape drawable. This must be the root element.
<p class="caps">attributes:</p>
<dl class="atn-list">
+ <dt><code>xmlns:android</code></dt>
+ <dd><em>String</em>. <strong>Required.</strong> Defines the XML namespace, which must be
+ <code>"http://schemas.android.com/apk/res/android"</code>.
<dt><code>android:shape</code></dt>
<dd><em>Keyword</em>. Defines the type of shape. Valid values are:
<table>
@@ -525,7 +1510,7 @@ href="more-resources.html#Dimension">dimension resource</a>.</dd>
<dd><em>Float</em>. The radius for the inner
part of the ring, expressed as a ratio of the ring's width. For instance, if {@code
android:innerRadiusRatio="5"}, then the inner radius equals the ring's width divided by 5. This
-value will be overridden by {@code android:innerRadius}. Default value is 9.</dd>
+value is overridden by {@code android:innerRadius}. Default value is 9.</dd>
<dt><code>android:thickness</code></dt>
<dd><em>Dimension</em>. The thickness of the
ring, as a dimension value or <a
@@ -533,13 +1518,40 @@ href="more-resources.html#Dimension">dimension resource</a>.</dd>
<dt><code>android:thicknessRatio</code></dt>
<dd><em>Float</em>. The thickness of the ring,
expressed as a ratio of the ring's width. For instance, if {@code android:thicknessRatio="2"}, then
-the thickness equals the ring's width divided by 2. This value will be overridden by {@code
+the thickness equals the ring's width divided by 2. This value is overridden by {@code
android:innerRadius}. Default value is 3.</dd>
<dt><code>android:useLevel</code></dt>
<dd><em>Boolean</em>. "true" if this is used as
a {@link android.graphics.drawable.LevelListDrawable}. This should normally be "false"
or your shape may not appear.</dd>
</dl>
+ <dt id="corners-element"><code>&lt;corners&gt;</code></dt>
+ <dd>Creates rounded corners for the shape. Applies only when the shape is a rectangle.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:radius</code></dt>
+ <dd><em>Dimension</em>. The radius for all corners, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>. This is overridden for each
+corner by the following attributes.</dd>
+ <dt><code>android:topLeftRadius</code></dt>
+ <dd><em>Dimension</em>. The radius for the top-left corner, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:topRightRadius</code></dt>
+ <dd><em>Dimension</em>. The radius for the top-right corner, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:bottomLeftRadius</code></dt>
+ <dd><em>Dimension</em>. The radius for the bottom-left corner, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:bottomRightRadius</code></dt>
+ <dd><em>Dimension</em>. The radius for the bottom-right corner, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ </dl>
+ <p class="note"><strong>Note:</strong> Every corner must (initially) be provided a corner
+radius greater than 1, or else no corners are rounded. If you want specific corners
+to <em>not</em> be rounded, a work-around is to use {@code android:radius} to set a default corner
+radius greater than 1, but then override each and every corner with the values you really
+want, providing zero ("0dp") where you don't want rounded corners.</p>
+ </dd>
<dt id="gradient-element"><code>&lt;gradient&gt;</code></dt>
<dd>Specifies a gradient color for the shape.
<p class="caps">attributes:</p>
@@ -582,6 +1594,42 @@ value or <a href="more-resources.html#Color">color resource</a>.</dd>
android.graphics.drawable.LevelListDrawable}.</dd>
</dl>
</dd>
+ <dt id="padding-element"><code>&lt;padding&gt;</code></dt>
+ <dd>Padding to apply to the containing View element (this pads the position of the View
+content, not the shape).
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:left</code></dt>
+ <dd><em>Dimension</em>. Left padding, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:top</code></dt>
+ <dd><em>Dimension</em>. Top padding, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:right</code></dt>
+ <dd><em>Dimension</em>. Right padding, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:bottom</code></dt>
+ <dd><em>Dimension</em>. Bottom padding, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ </dl>
+ </dd>
+ <dt id="solid-element"><code>&lt;size&gt;</code></dt>
+ <dd>The size of the shape.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>android:height</code></dt>
+ <dd><em>Dimension</em>. The height of the shape, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ <dt><code>android:width</code></dt>
+ <dd><em>Dimension</em>. The width of the shape, as a dimension value or <a
+href="more-resources.html#Dimension">dimension resource</a>.</dd>
+ </dl>
+ <p class="note"><strong>Note:</strong> The shape scales to the size of the container
+View proportionate to the dimensions defined here, by default. When you use the shape in an {@link
+android.widget.ImageView}, you can restrict scaling by setting the <a
+href="{@docRoot}reference/android/widget/ImageView.html#attr_android:scaleType">{@code
+android:scaleType}</a> to {@code "center"}.</p>
+ </dd>
<dt id="solid-element"><code>&lt;solid&gt;</code></dt>
<dd>A solid color to fill the shape.
<p class="caps">attributes:</p>
@@ -611,57 +1659,12 @@ href="more-resources.html#Dimension">dimension resource</a>. Only valid if {@cod
android:dashGap} is set.</dd>
</dl>
</dd>
- <dt id="padding-element"><code>&lt;padding&gt;</code></dt>
- <dd>Padding to apply to the containing View element (this pads the position of the View
-content, not the shape).
- <p class="caps">attributes:</p>
- <dl class="atn-list">
- <dt><code>android:left</code></dt>
- <dd><em>Dimension</em>. Left padding, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:top</code></dt>
- <dd><em>Dimension</em>. Top padding, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:right</code></dt>
- <dd><em>Dimension</em>. Right padding, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:bottom</code></dt>
- <dd><em>Dimension</em>. Bottom padding, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- </dl>
- </dd>
- <dt id="corners-element"><code>&lt;corners&gt;</code></dt>
- <dd>Creates rounded corners for the shape. Applies only when the shape is a rectangle.
- <p class="caps">attributes:</p>
- <dl class="atn-list">
- <dt><code>android:radius</code></dt>
- <dd><em>Dimension</em>. The radius for all corners, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>. This will be overridden for each
-corner by the following attributes.</dd>
- <dt><code>android:topLeftRadius</code></dt>
- <dd><em>Dimension</em>. The radius for the top-left corner, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:topRightRadius</code></dt>
- <dd><em>Dimension</em>. The radius for the top-right corner, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:bottomLeftRadius</code></dt>
- <dd><em>Dimension</em>. The radius for the bottom-left corner, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- <dt><code>android:bottomRightRadius</code></dt>
- <dd><em>Dimension</em>. The radius for the bottom-right corner, as a dimension value or <a
-href="more-resources.html#Dimension">dimension resource</a>.</dd>
- </dl>
- <p class="note"><strong>Note:</strong> Every corner must (initially) be provided a corner
-radius greater than zero, or else no corners will be rounded. If you want specific corners
-to <em>not</em> be rounded, a work-around is to use {@code android:radius} to set a default corner
-radius greater than zero, but then override each and every corner with the values you really
-want, providing zero ("0dp") where you don't want rounded corners.</p>
- </dd>
</dl>
</dd> <!-- end elements and attributes -->
<dt>example:</dt>
+
<dd>XML file saved at <code>res/drawable/gradient_box.xml</code>:
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
@@ -678,14 +1681,16 @@ want, providing zero ("0dp") where you don't want rounded corners.</p>
&lt;corners android:radius="8dp" />
&lt;/shape>
</pre>
- <p>This layout XML will apply the shape drawable to a View:</p>
+
+ <p>This layout XML applies the shape drawable to a View:</p>
<pre>
&lt;TextView
android:background="@drawable/gradient_box"
android:layout_height="wrap_content"
android:layout_width="wrap_content" />
</pre>
- <p>This application code will get the shape drawable and apply it to a View:</p>
+
+ <p>This application code gets the shape drawable and applies it to a View:</p>
<pre>
Resources res = {@link android.content.Context#getResources()};
Drawable shape = res. {@link android.content.res.Resources#getDrawable(int) getDrawable}(R.drawable.gradient_box);
@@ -695,6 +1700,14 @@ tv.setBackground(shape);
</pre>
</dd> <!-- end example -->
+<dt>see also:</dt>
+
+<dd>
+<ul>
+ <li>{@link android.graphics.drawable.ShapeDrawable}</li>
+</ul>
+</dd>
+
</dl>
diff --git a/docs/html/guide/topics/resources/layout-resource.jd b/docs/html/guide/topics/resources/layout-resource.jd
index 0688a18..111851c 100644
--- a/docs/html/guide/topics/resources/layout-resource.jd
+++ b/docs/html/guide/topics/resources/layout-resource.jd
@@ -35,12 +35,12 @@ In XML: <code>@[<em>package</em>:]layout/<em>filename</em></code>
<pre class="stx">
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;<a href="#viewgroup-element"><em>ViewGroup</em></a> xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/<em>name</em>"
+ android:id="@[+][<em>package</em>:]id/<em>resource_name</em>"
android:layout_height=["<em>dimension</em>" | "fill_parent" | "wrap_content"]
android:layout_width=["<em>dimension</em>" | "fill_parent" | "wrap_content"]
[<em>ViewGroup-specific attributes</em>] &gt;
&lt;<a href="#view-element"><em>View</em></a>
- android:id="@+id/<em>name</em>"
+ android:id="@[+][<em>package</em>:]id/<em>resource_name</em>"
android:layout_height=["<em>dimension</em>" | "fill_parent" | "wrap_content"]
android:layout_width=["<em>dimension</em>" | "fill_parent" | "wrap_content"]
[<em>View-specific attributes</em>] &gt;
@@ -49,10 +49,12 @@ In XML: <code>@[<em>package</em>:]layout/<em>filename</em></code>
&lt;<a href="#viewgroup-element"><em>ViewGroup</em></a> &gt;
&lt;<a href="#view-element"><em>View</em></a> /&gt;
&lt;/<em>ViewGroup</em>&gt;
+ &lt;<a href="#include-element">include</a> layout="@layout/<i>layout_resource</i>"/&gt;
&lt;/<em>ViewGroup</em>&gt;
</pre>
<p class="note"><strong>Note:</strong> The root element can be either a
-{@link android.view.ViewGroup} or a {@link android.view.View}, but there must be only
+{@link android.view.ViewGroup}, a {@link android.view.View}, or a <a
+href="#merge-element">{@code &lt;merge&gt;}</a> element, but there must be only
one root element and it must contain the {@code xmlns:android} attribute with the {@code android}
namespace as shown.</p>
</dd>
@@ -74,10 +76,9 @@ namespace as shown.</p>
<p class="caps">attributes:</p>
<dl class="atn-list">
<dt><code>android:id</code></dt>
- <dd><em>Resource name</em>. A unique resource name for the element, which you can
-use to obtain a reference to the {@link android.view.ViewGroup} from your application.
- The value takes the form: <code>"@+id/<em>name</em>"</code>. See more about the
- <a href="#idvalue">value for {@code android:id}</a> below.
+ <dd><em>Resource ID</em>. A unique resource name for the element, which you can
+use to obtain a reference to the {@link android.view.ViewGroup} from your application. See more
+about the <a href="#idvalue">value for {@code android:id}</a> below.
</dd>
<dt><code>android:layout_height</code></dt>
<dd><em>Dimension or keyword</em>. <strong>Required</strong>. The height for the group, as a
@@ -107,10 +108,9 @@ attributes</a>).</p>
<p class="caps">attributes:</p>
<dl class="atn-list">
<dt><code>android:id</code></dt>
- <dd><em>Resource name</em>. A unique resource name for the element, which you can use to
- obtain a reference to the {@link android.view.View} from your application.
- The value takes the form: <code>"@+id/<em>name</em>"</code>. See more about the
- <a href="#idvalue">value for {@code android:id}</a> below.
+ <dd><em>Resource ID</em>. A unique resource name for the element, which you can use to
+ obtain a reference to the {@link android.view.View} from your application. See more about
+the <a href="#idvalue">value for {@code android:id}</a> below.
</dd>
<dt><code>android:layout_height</code></dt>
<dd><em>Dimension or keyword</em>. <strong>Required</strong>. The height for the element, as
@@ -137,20 +137,71 @@ or {@code "wrap_content"}). See the <a href="#layoutvalues">valid values</a> bel
which gives it's parent initial focus on the screen. You can have only one of these
elements per file.</dd>
+ <dt id="include-element"><code>&lt;include&gt;</code></dt>
+ <dd>Includes a layout file into this layout.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>layout</code></dt>
+ <dd><em>Layout resource</em>. <strong>Required</strong>. Reference to a layout
+resource.</dd>
+ <dt><code>android:id</code></dt>
+ <dd><em>Resource ID</em>. Overrides the ID given to the root view in the included layout.
+ </dd>
+ <dt><code>android:layout_height</code></dt>
+ <dd><em>Dimension or keyword</em>. Overrides the height given to the root view in the
+included layout.
+ </dd>
+ <dt><code>android:layout_width</code></dt>
+ <dd><em>Dimension or keyword</em>. Overrides the width given to the root view in the
+included layout.
+ </dd>
+ </dl>
+ <p>You can include any other layout attributes in the <code>&lt;include&gt;</code> that are
+supported by the root element in the included layout and they will override those defined in the
+root element.</p>
+
+ <p>Another way to include a layout is to use {@link android.view.ViewStub}. It is a lightweight
+View that consumes no layout space until you explicitly inflate it, at which point, it includes a
+layout file defined by its {@code android:layout} attribute. For more information about using {@link
+android.view.ViewStub}, read <a href="{@docRoot}resources/articles/layout-tricks-stubs.html">Layout
+Tricks: ViewStubs</a>.</p>
+ </dd>
+
+ <dt id="merge-element"><code>&lt;merge&gt;</code></dt>
+ <dd>An alternative root element that is not drawn in the layout hierarchy. Using this as the
+root element is useful when you know that this layout will be placed into a layout
+that already contains the appropriate parent View to contain the children of the
+<code>&lt;merge&gt;</code> element. This is particularly useful when you plan to include this layout
+in another layout file using <a href="#include-element"><code>&lt;include&gt;</code></a> and
+this layout doesn't require a different {@link android.view.ViewGroup} container. For more
+information about merging layouts, read <a
+href="{@docRoot}resources/articles/layout-tricks-merging.html">Layout
+Tricks: Merging</a>.</dd>
+
</dl>
+
+
<h4 id="idvalue">Value for <code>android:id</code></h4>
-<p>For the ID value, you should use this syntax form: <code>"@+id/<em>name</em>"</code>. The plus symbol,
-{@code +}, indicates that this is a new resource ID and the aapt tool will create
-a new resource number to the {@code R.java} class, if it doesn't already exist. For example:</p>
+<p>For the ID value, you should usually use this syntax form: <code>"@+id/<em>name</em>"</code>. The
+plus symbol, {@code +}, indicates that this is a new resource ID and the <code>aapt</code> tool will
+create a new resource integer in the {@code R.java} class, if it doesn't already exist. For
+example:</p>
<pre>
&lt;TextView android:id="@+id/nameTextbox"/>
</pre>
-<p>You can then refer to it this way in Java:</p>
+<p>The <code>nameTextbox</code> name is now a resource ID attached to this element. You can then
+refer to the {@link android.widget.TextView} to which the ID is associated in Java:</p>
<pre>
findViewById(R.id.nameTextbox);
</pre>
+<p>This code returns the {@link android.widget.TextView} object.</p>
+
+<p>However, if you have already defined an <a
+href="{@docRoot}guide/topics/resources/drawable-resource.html#Id">ID resource</a> (and it is not
+already used), then you can apply that ID to a {@link android.view.View} element by excluding the
+plus symbol in the <code>android:id</code> value.</p>
<h4 id="layoutvalues">Value for <code>android:layout_height</code> and
<code>android:layout_width</code>:</h4>
diff --git a/docs/html/guide/topics/resources/menu-resource.jd b/docs/html/guide/topics/resources/menu-resource.jd
index badc403..cde72bd 100644
--- a/docs/html/guide/topics/resources/menu-resource.jd
+++ b/docs/html/guide/topics/resources/menu-resource.jd
@@ -35,7 +35,7 @@ In XML: <code>@[<em>package</em>:]menu.<em>filename</em></code>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;<a href="#menu-element">menu</a> xmlns:android="http://schemas.android.com/apk/res/android">
- &lt;<a href="#item-element">item</a> android:id="@+id/<em>id_name</em>"
+ &lt;<a href="#item-element">item</a> android:id="@[+][<em>package</em>:]id/<em>resource_name</em>"
android:menuCategory=["container" | "system" | "secondary" | "alternative"]
android:orderInCategory="<em>integer</em>"
android:title="<em>string</em>"
@@ -46,7 +46,7 @@ In XML: <code>@[<em>package</em>:]menu.<em>filename</em></code>
android:checkable=["true" | "false"]
android:visible=["visible" | "invisible" | "gone"]
android:enabled=["enabled" | "disabled"] /&gt;
- &lt;<a href="#group-element">group</a> android:id="<em>resource ID</em>"
+ &lt;<a href="#group-element">group</a> android:id="@[+][<em>package</em>:]id/<em>resource name</em>"
android:menuCategory=["container" | "system" | "secondary" | "alternative"]
android:orderInCategory="<em>integer</em>"
android:checkableBehavior=["none" | "all" | "single"]
@@ -84,8 +84,8 @@ child of a <code>&lt;menu&gt;</code> element.
<p class="caps">attributes:</p>
<dl class="atn-list">
<dt><code>android:id</code></dt>
- <dd><em>Resource name</em>. A unique resource name. The value takes the form:
-<code>"@+id/<em>name</em>"</code>.</dd>
+ <dd><em>Resource ID</em>. A unique resource ID. To create a new resource ID for this item, use the form:
+<code>"@+id/<em>name</em>"</code>. The plus symbol indicates that this should be created as a new ID.</dd>
<dt><code>android:menuCategory</code></dt>
<dd><em>Keyword</em>. Value corresponding to {@link android.view.Menu} {@code CATEGORY_*}
constants, which define the group's priority. Valid values:
@@ -124,8 +124,8 @@ on the data that is currently displayed.</td></tr>
<p class="caps">attributes:</p>
<dl class="atn-list">
<dt><code>android:id</code></dt>
- <dd><em>Resource name</em>. A unique resource name. The value takes the form:
-<code>"@+id/<em>name</em>"</code>.</dd>
+ <dd><em>Resource ID</em>. A unique resource ID. To create a new resource ID for this item, use the form:
+<code>"@+id/<em>name</em>"</code>. The plus symbol indicates that this should be created as a new ID.</dd>
<dt><code>android:menuCategory</code></dt>
<dd><em>Keyword</em>. Value corresponding to {@link android.view.Menu} {@code CATEGORY_*}
constants, which define the item's priority. Valid values:
diff --git a/docs/html/guide/topics/resources/more-resources.jd b/docs/html/guide/topics/resources/more-resources.jd
index 0e2b30b..a647571 100644
--- a/docs/html/guide/topics/resources/more-resources.jd
+++ b/docs/html/guide/topics/resources/more-resources.jd
@@ -12,6 +12,9 @@ parent.link=available-resources.html
<dd>XML resource that carries a color value (a hexadecimal color).</dd>
<dt><a href="#Dimension">Dimension</a></dt>
<dd>XML resource that carries a dimension value (with a unit of measure).</dd>
+ <dt><a href="#Id">ID</a></dt>
+ <dd>XML resource that provides a unique identifier for application resources and
+components.</dd>
<dt><a href="#Integer">Integer</a></dt>
<dd>XML resource that carries an integer value.</dd>
<dt><a href="#IntegerArray">Integer Array</a></dt>
@@ -111,8 +114,9 @@ boolean screenIsSmall = res.{@link android.content.res.Resources#getBoolean(int)
<h2 id="Color">Color</h2>
<p>A color value defined in XML.
-The color is specified with an RGB value and alpha channel. A color resource can be used
-any place that expects a hexadecimal color value.</p>
+The color is specified with an RGB value and alpha channel. You can use a color resource
+any place that accepts a hexadecimal color value. You can also use a color resource when a
+drawable resource is expected in XML (for example, {@code android:drawable="@color/green"}).</p>
<p>The value always begins with a pound (#) character and then followed by the
Alpha-Red-Green-Blue information in one of the following formats:</p>
@@ -306,7 +310,7 @@ float fontSize = res.{@link android.content.res.Resources#getDimension(int) getD
&lt;TextView
android:layout_height="@dimen/textview_height"
android:layout_width="@dimen/textview_width"
- android:textSize="@dimen/sixteen_sp"/>
+ android:textSize="@dimen/font_size"/>
</pre>
</dl>
</dd> <!-- end example -->
@@ -318,6 +322,118 @@ float fontSize = res.{@link android.content.res.Resources#getDimension(int) getD
+<h2 id="Id">ID</h2>
+
+<p>A unique resource ID defined in XML. Using the name you provide in the {@code &lt;item&gt;}
+element, the Android developer tools create a unique integer in your project's {@code
+R.java} class, which you can use as an
+identifier for an application resources (for example, a {@link android.view.View} in your UI layout)
+or a unique integer for use in your application code (for example, as an ID for a dialog or a
+result code).</p>
+
+<p class="note"><strong>Note:</strong> An ID is a simple resource that is referenced
+using the value provided in the {@code name} attribute (not the name of the XML file). As
+such, you can combine ID resources with other simple resources in the one XML file,
+under one {@code &lt;resources&gt;} element. Also, remember that an ID resources does not reference
+an actual resource item; it is simply a unique ID that you can attach to other resources or use
+as a unique integer in your application.</p>
+
+<dl class="xml">
+
+<dt>file location:</dt>
+<dd><code>res/values/<em>filename.xml</em></code><br/>
+The filename is arbitrary.</dd>
+
+<dt>resource reference:</dt>
+<dd>
+In Java: <code>R.id.<em>name</em></code><br/>
+In XML: <code>@[<em>package</em>:]id/<em>name</em></code>
+</dd>
+
+<dt>syntax:</dt>
+<dd>
+<pre class="stx">
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;<a href="#id-resources-element">resources</a>&gt;
+ &lt;<a href="#id-item-element">item</a>
+ type="id"
+ name="<em>id_name</em>" /&gt;
+&lt;/resources&gt;
+</pre>
+</dd>
+
+<dt>elements:</dt>
+<dd>
+<dl class="tag-list">
+
+ <dt id="integer-resources-element"><code>&lt;resources&gt;</code></dt>
+ <dd><strong>Required.</strong> This must be the root node.
+ <p>No attributes.</p>
+ </dd>
+ <dt id="integer-element"><code>&lt;integer&gt;</code></dt>
+ <dd>Defines a unique ID. Takes no value, only attributes.
+ <p class="caps">attributes:</p>
+ <dl class="atn-list">
+ <dt><code>type</code></dt>
+ <dd>Must be "id".</dd>
+ <dt><code>name</code></dt>
+ <dd><em>String</em>. A unique name for the ID.</dd>
+ </dl>
+ </dd>
+
+</dl>
+</dd> <!-- end elements and attributes -->
+
+<dt>example:</dt>
+<dd>
+ <p>XML file saved at <code>res/values/ids.xml</code>:</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;resources>
+ &lt;item type="id" name="button_ok" /&gt;
+ &lt;item type="id" name="dialog_exit" /&gt;
+&lt;/resources>
+</pre>
+
+ <p>Then, this layout snippet uses the "button_ok" ID for a Button widget:</p>
+<pre>
+&lt;Button android:id="<b>@id/button_ok</b>"
+ style="@style/button_style" /&gt;
+</pre>
+
+ <p>Notice that the {@code android:id} value does not include the plus sign in the ID reference,
+because the ID already exists, as defined in the {@code ids.xml} example above. (When you specify an
+ID to an XML resource using the plus sign&mdash;in the format {@code
+android:id="@+id/name"}&mdash;it means that the "name" ID does not exist and should be created.)</p>
+
+ <p>As another example, the following code snippet uses the "dialog_exit" ID as a unique identifier
+for a dialog:</p>
+<pre>
+{@link android.app.Activity#showDialog(int) showDialog}(<b>R.id.dialog_exit</b>);
+</pre>
+ <p>In the same application, the "dialog_exit" ID is compared when creating a dialog:</p>
+<pre>
+protected Dialog {@link android.app.Activity#onCreateDialog(int)}(int id) {
+ Dialog dialog;
+ switch(id) {
+ case <b>R.id.dialog_exit</b>:
+ ...
+ break;
+ default:
+ dialog = null;
+ }
+ return dialog;
+}
+</pre>
+</dd> <!-- end example -->
+
+
+</dl>
+
+
+
+
+
<h2 id="Integer">Integer</h2>
<p>An integer defined in XML.</p>
@@ -347,7 +463,7 @@ In XML: <code>@[<em>package</em>:]integer/<em>integer_name</em></code>
&lt;<a href="#integer-resources-element">resources</a>>
&lt;<a href="#integer-element">integer</a>
name="<em>integer_name</em>"
- &gt;<em>integer</em>&lt;/dimen&gt;
+ &gt;<em>integer</em>&lt;/integer&gt;
&lt;/resources&gt;
</pre>
</dd>
@@ -379,8 +495,8 @@ In XML: <code>@[<em>package</em>:]integer/<em>integer_name</em></code>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;resources>
- &lt;integer name="max_speed">75&lt;/dimen>
- &lt;integer name="min_speed">5&lt;/dimen>
+ &lt;integer name="max_speed">75&lt;/integer>
+ &lt;integer name="min_speed">5&lt;/integer>
&lt;/resources>
</pre>
<p>This application code retrieves an integer:</p>
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index cac85e8..1d6ab25 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -450,8 +450,8 @@ application during runtime.</p>
to match the device density.</li>
</ul>
<p><em>Added in API Level 4.</em></p>
- <p>There is thus a 4:3 scaling factor between each density, so a 9x9 bitmap
- in ldpi is 12x12 in mdpi and 16x16 in hdpi.</p>
+ <p>There is thus a 3:4:6 scaling ratio between the three densities, so a 9x9 bitmap
+ in ldpi is 12x12 in mdpi and 18x18 in hdpi.</p>
<p>When Android selects which resource files to use,
it handles screen density differently than the other qualifiers.
In step 1 of <a href="#BestMatch">How Android finds the best
@@ -761,7 +761,7 @@ Android runs your application, it will crash if you do not provide default resou
cannot use the resources named with the new qualifier. For example, if your <a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
minSdkVersion}</a> is set to 4, and you qualify all of your drawable resources using <a
-href="NightQualifier">night mode</a> ({@code night} or {@code notnight}, which were added in API
+href="#NightQualifier">night mode</a> ({@code night} or {@code notnight}, which were added in API
Level 8), then an API Level 4 device cannot access your drawable resources and will crash. In this
case, you probably want {@code notnight} to be your default resources, so you should exclude that
qualifier so your drawable resources are in either {@code drawable/} or {@code drawable-night/}.</p>
@@ -895,7 +895,7 @@ drawable-port-ldpi/
drawable-port-notouch-12key/
</pre>
<p class="note"><strong>Exception:</strong> Screen pixel density is the one qualifier that is not
-eliminated due to a contradiction. Even though the screen density of the device is mdpi,
+eliminated due to a contradiction. Even though the screen density of the device is hdpi,
<code>drawable-port-ldpi/</code> is not eliminated because every screen density is
considered to be a match at this point. More information is available in the <a
href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
@@ -922,9 +922,8 @@ drawable-en-notouch-12key/
<strike>drawable-port-notouch-12key/</strike>
</pre>
<p class="note"><strong>Exception:</strong> If the qualifier in question is screen pixel density,
-Android
-selects the option that most closely matches the device, and the selection process is complete.
-In general, Android prefers scaling down a larger original image to scaling up a smaller
+Android selects the option that most closely matches the device screen density.
+In general, Android prefers scaling down a larger original image to scaling up a smaller
original image. See <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple
Screens</a>.</p>
</li>
diff --git a/docs/html/guide/topics/search/adding-custom-suggestions.jd b/docs/html/guide/topics/search/adding-custom-suggestions.jd
index 9ea4c8b..ce0c619 100644
--- a/docs/html/guide/topics/search/adding-custom-suggestions.jd
+++ b/docs/html/guide/topics/search/adding-custom-suggestions.jd
@@ -5,74 +5,88 @@ parent.link=index.html
<div id="qv-wrapper">
<div id="qv">
-<h2>Key classes</h2>
-<ol>
-<li>{@link android.app.SearchManager}</li>
-<li>{@link android.content.SearchRecentSuggestionsProvider}</li>
-<li>{@link android.content.ContentProvider}</li>
-</ol>
<h2>In this document</h2>
<ol>
<li><a href="#TheBasics">The Basics</a></li>
-<li><a href="#CustomSearchableConfiguration">Modifying the searchable configuration</a></li>
+<li><a href="#CustomSearchableConfiguration">Modifying the Searchable Configuration</a></li>
<li><a href="#CustomContentProvider">Creating a Content Provider</a>
<ol>
<li><a href="#HandlingSuggestionQuery">Handling a suggestion query</a></li>
<li><a href="#SuggestionTable">Building a suggestion table</a></li>
</ol>
</li>
-<li><a href="#IntentForSuggestions">Declaring an Intent for suggestions</a>
+<li><a href="#IntentForSuggestions">Declaring an Intent for Suggestions</a>
<ol>
<li><a href="#IntentAction">Declaring the Intent action</a></li>
<li><a href="#IntentData">Declaring the Intent data</a></li>
</ol>
</li>
<li><a href="#HandlingIntent">Handling the Intent</a></li>
-<li><a href="#RewritingQueryText">Rewriting the query text</a></li>
-<li><a href="#QSB">Exposing search suggestions to Quick Search Box</a></li>
+<li><a href="#RewritingQueryText">Rewriting the Query Text</a></li>
+<li><a href="#QSB">Exposing Search Suggestions to Quick Search Box</a></li>
+</ol>
+
+<h2>Key classes</h2>
+<ol>
+<li>{@link android.app.SearchManager}</li>
+<li>{@link android.content.SearchRecentSuggestionsProvider}</li>
+<li>{@link android.content.ContentProvider}</li>
+</ol>
+
+<h2>Related Samples</h2>
+<ol>
+<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
+Dictionary</a></li>
</ol>
+
<h2>See also</h2>
<ol>
<li><a href="searchable-config.html">Searchable Configuration</a></li>
<li><a href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a></li>
-<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
-Dictionary sample app</a></li>
</ol>
</div>
</div>
-<p>The Android search framework provides the ability for your application to
-provide suggestions while the user types into the Android search dialog. In this guide, you'll learn
-how to create custom suggestions. These are suggestions based on custom data provided by your
-application. For example, if your application is a word dictionary, you can suggest words from the
-dictionary that match the text entered so far. These are the most valuable suggestions because you
-can effectively predict what the user wants and provide instant access to it. Once you provide
-custom suggestions, you then make them available to the system-wide Quick Search Box, providing
-access to your content from outside your application.</p>
-
-<p>Before you begin, you need to have implemented the Android search dialog for searches in your
-application. If you haven't done this, see <a href="search-dialog.html">Using the Android Search
+<p>When using the Android search dialog, you can provide custom search suggestions that are
+created from data in your application. For example, if your application is a word
+dictionary, you can suggest words from the
+dictionary that match the text entered so far. These are the most valuable suggestions, because you
+can effectively predict what the user wants and provide instant access to it. Figure 1 shows
+an example of a search dialog with custom suggestions.</p>
+
+<p>Once you provide custom suggestions, you can also make them available to the system-wide Quick
+Search Box, providing access to your content from outside your application.</p>
+
+<p>Before you begin with this guide to add custom suggestions, you need to have implemented the
+Android search dialog for searches in your
+application. If you haven't, see <a href="search-dialog.html">Using the Android Search
Dialog</a>.</p>
<h2 id="TheBasics">The Basics</h2>
-<img src="{@docRoot}images/search/search-suggest-custom.png" alt="" height="417"
-style="float:right;clear:right;" />
+<div class="figure" style="width:250px">
+<img src="{@docRoot}images/search/search-suggest-custom.png" alt="" height="417" />
+<p class="img-caption"><strong>Figure 1.</strong> Screenshot of a search dialog with custom
+search suggestions.</p>
+</div>
-<p>When the user selects a custom suggestions, the Search Manager will send a customized Intent to
-your searchable Activity. Whereas a normal search query will send an Intent with the {@link
+<p>When the user selects a custom suggestion, the Search Manager sends an {@link
+android.content.Intent} to
+your searchable Activity. Whereas a normal search query sends an Intent with the {@link
android.content.Intent#ACTION_SEARCH} action, you can instead define your custom suggestions to use
-{@link android.content.Intent#ACTION_VIEW} (or any other action), and also include additional data
+{@link android.content.Intent#ACTION_VIEW} (or any other Intent action), and also include data
that's relevant to the selected suggestion. Continuing
the dictionary example, when the user selects a suggestion, your application can immediately
open the definition for that word, instead of searching the dictionary for matches.</p>
-<p>To provide custom suggestions, you need to do the following:</p>
+<p>To provide custom suggestions, do the following:</p>
<ul>
<li>Implement a basic searchable Activity, as described in <a
href="search-dialog.html">Using the Android Search Dialog</a>.</li>
+ <li>Modify the searchable configuration with information about the content provider that
+provides custom suggestions.</li>
<li>Build a table (such as in an {@link android.database.sqlite.SQLiteDatabase}) for your
suggestions and format the table with required columns.</li>
<li>Create a <a href="{@docRoot}guide/topics/providers/content-providers.html">Content
@@ -80,36 +94,32 @@ Provider</a> that has access to your suggestions table and declare the provider
in your manifest.</li>
<li>Declare the type of {@link android.content.Intent} to be sent when the user selects a
suggestion (including a custom action and custom data). </li>
- <li>Modify the searchable configuration with information about the content provider.</li>
</ul>
-<p>Just like the Search Manager handles the rendering of the search dialog, it will also do the work
-to display all search suggestions below the search dialog. All you need to do is provide a source
-from which the suggestions can be retrieved.</p>
-
-<p class="note"><strong>Note:</strong> If you're not familiar with creating Content
-Providers, please read the <a href="{@docRoot}guide/topics/providers/content-providers.html">Content
+<p>Just like the Search Manager displays the search dialog, it also displays your search
+suggestions. All you need is a content provider from which the Search Manager can retrieve your
+suggestions. If you're not familiar with creating content
+providers, read the <a href="{@docRoot}guide/topics/providers/content-providers.html">Content
Providers</a> developer guide before you continue.</p>
-<p>When the Search Manager identifies that your Activity is searchable and also provides search
-suggestions, the following procedure will take place as soon as the user types into the Android
-search box:</p>
+<p>When the Search Manager identifies that your Activity is searchable and provides search
+suggestions, the following procedure takes place as soon as the user enters text into the
+search dialog:</p>
-<ul>
- <li>The Search Manager takes the search query text (whatever has been typed so far) and performs a
-query to the content provider that manages your suggestions.</li>
- <li>Your content provider then returns a {@link android.database.Cursor} that points to all
+<ol>
+ <li>Search Manager takes the search query text (whatever has been typed so far) and performs a
+query to your content provider that manages your suggestions.</li>
+ <li>Your content provider returns a {@link android.database.Cursor} that points to all
suggestions that are relevant to the search query text.</li>
- <li>The Search Manager then displays the list of suggestions provided by the Cursor (as
-demonstrated in the screenshot to the right).</li>
-</ul>
+ <li>Search Manager displays the list of suggestions provided by the Cursor.</li>
+</ol>
-<p>At this point, the following may happen:</p>
+<p>Once the custom suggestions are displayed, the following might happen:</p>
<ul>
<li>If the user types another key, or changes the query in any way, the above steps are repeated
and the suggestion list is updated as appropriate. </li>
- <li>If the user executes the search, the suggestions are ignored and the search is delivered
+ <li>If the user executes the search, the suggestions are ignored and the search is delivered
to your searchable Activity using the normal {@link android.content.Intent#ACTION_SEARCH}
Intent.</li>
<li>If the user selects a suggestion, an Intent is sent to your searchable Activity, carrying a
@@ -124,56 +134,64 @@ custom action and custom data so that your application can open the suggested co
to the {@code &lt;searchable&gt;} element in your searchable configuration file. For example:</p>
<pre>
-&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_label"
android:hint="@string/search_hint"
- android:searchSuggestAuthority="my.package.MyCustomSuggestionProvider" >
-&lt;/searchable>
+ <b>android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider"</b>&gt;
+&lt;/searchable&gt;
</pre>
-<p>You may require some additional attributes, depending on the type of Intent you attach
+<p>You might need some additional attributes, depending on the type of Intent you attach
to each suggestion and how you want to format queries to your content provider. The other optional
-attributes are discussed in the relevant sections below.</p>
+attributes are discussed in the following sections.</p>
+
<h2 id="CustomContentProvider">Creating a Content Provider</h2>
-<p>Creating a content provider for custom suggestions requires previous knowledge about Content
-Providers that's covered in the <a
+<p>Creating a content provider for custom suggestions requires previous knowledge about content
+providers that's covered in the <a
href="{@docRoot}guide/topics/providers/content-providers.html">Content Provider</a> developer
guide. For the most part, a content provider for custom suggestions is the
same as any other content provider. However, for each suggestion you provide, the respective row in
the {@link android.database.Cursor} must include specific columns that the Search Manager
-understands.</p>
+understands and uses to format the suggestions.</p>
-<p>When the user starts typing into the search dialog, the Search Manager will query your Content
-Provider for suggestions by calling {@link
+<p>When the user starts typing into the search dialog, the Search Manager queries your content
+provider for suggestions by calling {@link
android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} each time
a letter is typed. In your implementation of {@link
android.content.ContentProvider#query(Uri,String[],String,String[],String) query()}, your
content provider must search your suggestion data and return a {@link
-android.database.Cursor} that points to the rows you determine to be good suggestions.</p>
+android.database.Cursor} that points to the rows you have determined to be good suggestions.</p>
-<p>The following two sections describe how the Search Manager will send requests to your Content
-Provider and how you can handle them, and define the columns that the Search Manager understands and
-expects to be provided in the {@link android.database.Cursor} returned with each query.</p>
+<p>Details about creating a content provider for custom suggestions are discussed in the following
+two sections:</p>
+<dl>
+ <dt><a href="#HandlingSuggestionQuery">Handling the suggestion query</a></dt>
+ <dd>How the Search Manager sends requests to your content provider and how to handle them</dd>
+ <dt><a href="#SuggestionTable">Building a suggestion table</a></dt>
+ <dd>How to define the columns that the Search Manager expects in the {@link
+android.database.Cursor} returned with each query</dd>
+</dl>
<h3 id="HandlingSuggestionQuery">Handling the suggestion query</h3>
-<p>When the Search Manager makes a request for suggestions from your content provider, it will call
-{@link android.content.ContentProvider#query(Uri,String[],String,String[],String)}. You must
-implement this method in your content provider so that it will search your suggestions and return a
-Cursor that contains the suggestions you deem relevant.</p>
+<p>When the Search Manager requests suggestions from your content provider, it calls your content
+provider's {@link android.content.ContentProvider#query(Uri,String[],String,String[],String)
+query()} method. You must
+implement this method to search your suggestion data and return a
+{@link android.database.Cursor} pointing to the suggestions you deem relevant.</p>
-<p>Here's a summary of the parameters that the Search Manager will pass to your {@link
+<p>Here's a summary of the parameters that the Search Manager passes to your {@link
android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method
(listed in order):</p>
<dl>
<dt><code>uri</code></dt>
- <dd>This will always be a content {@link android.net.Uri}, formatted as:
+ <dd>Always a content {@link android.net.Uri}, formatted as:
<pre class="no-pretty-print">
content://<em>your.authority</em>/<em>optional.suggest.path</em>/<em>{@link
android.app.SearchManager#SUGGEST_URI_PATH_QUERY}</em>
@@ -184,46 +202,48 @@ For example:</p>
content://<em>your.authority</em>/<em>optional.suggest.path</em>/<em>{@link
android.app.SearchManager#SUGGEST_URI_PATH_QUERY}</em>/puppies
</pre>
-<p>The query text on the end will be encoded using URI encoding rules, so you may need to decode
-it.</p>
+<p>The query text on the end is encoded using URI encoding rules, so you might need to decode
+it before performing a search.</p>
<p>The <em>{@code optional.suggest.path}</em> portion is only included in the URI if you have set
such a path in your searchable configuration file with the {@code android:searchSuggestPath}
attribute. This is only needed if you use the same content provider for multiple searchable
-activities, in which case you need to disambiguate the source of the suggestion query.</p>
-<p>Note that {@link android.app.SearchManager#SUGGEST_URI_PATH_QUERY} is not the literal
+activities, in which case, you need to disambiguate the source of the suggestion query.</p>
+<p class="note"><strong>Note:</strong> {@link
+android.app.SearchManager#SUGGEST_URI_PATH_QUERY} is not the literal
string provided in the URI, but a constant that you should use if you need to refer to this
path.</p>
</dd>
<dt><code>projection</code></dt>
- <dd>This is always null</dd>
+ <dd>Always null</dd>
<dt><code>selection</code></dt>
- <dd>This is the value provided in the {@code android:searchSuggestSelection} attribute of
+ <dd>The value provided in the {@code android:searchSuggestSelection} attribute of
your searchable configuration file, or null if you have not declared the {@code
-android:searchSuggestSelection} attribute. More about this below.</dd>
+android:searchSuggestSelection} attribute. More about using this to <a href="#GetTheQuery">get the
+query</a> below.</dd>
<dt><code>selectionArgs</code></dt>
- <dd>This contains the search query as the first (and only) element of the array if you have
+ <dd>Contains the search query as the first (and only) element of the array if you have
declared the {@code android:searchSuggestSelection} attribute in your searchable configuration. If
you have not declared {@code android:searchSuggestSelection}, then this parameter is null. More
-about this below.</dd>
+about using this to <a href="#GetTheQuery">get the query</a> below.</dd>
<dt><code>sortOrder</code></dt>
- <dd>This is always null</dd>
+ <dd>Always null</dd>
</dl>
-<p>As you may have realized, there are two ways by which the Search Manager can send you the search
-query text. The default manner is for the query text to be included as the last path of the content
-URI that is passed in the {@code uri} parameter. However, if you include a selection value in your
+<p>The Search Manager can send you the search query text in two ways. The
+default manner is for the query text to be included as the last path of the content
+URI passed in the {@code uri} parameter. However, if you include a selection value in your
searchable configuration's {@code
-android:searchSuggestSelection} attribute, then the query text will instead be passed as the first
-element of the {@code selectionArgs} string array. Both options are summarized below.</p>
+android:searchSuggestSelection} attribute, then the query text is instead passed as the first
+element of the {@code selectionArgs} string array. Both options are summarized next.</p>
-<h4>Get the query in the Uri</h4>
+<h4 id="GetTheQueryUri">Get the query in the Uri</h4>
-<p>By default, the query will be appended as the last segment of the {@code uri}
+<p>By default, the query is appended as the last segment of the {@code uri}
parameter (a {@link android.net.Uri} object). To retrieve the query text in this case, simply use
{@link android.net.Uri#getLastPathSegment()}. For example:</p>
@@ -231,52 +251,59 @@ parameter (a {@link android.net.Uri} object). To retrieve the query text in this
String query = uri.getLastPathSegment().toLowerCase();
</pre>
-<p>This will return the last segment of the Uri, which is the query text entered in the search
-dialog.</p>
+<p>This returns the last segment of the {@link android.net.Uri}, which is the query text entered in
+the search dialog.</p>
-<h4>Get the query in the selection arguments</h4>
+<h4 id="GetTheQuery">Get the query in the selection arguments</h4>
-<p>Instead of using the URI, you may decide it makes more sense for your {@link
+<p>Instead of using the URI, you might decide it makes more sense for your {@link
android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method to
receive everything it needs to perform the look-up and you want the
-{@code selection} and {@code selectionArgs} parameters to carry values. In this case, you can
-add the {@code android:searchSuggestSelection} attribute to your searchable configuration with your
-SQLite selection string. In this selection string, you can include a question mark ("?") as
-a placeholder for the actual search query. This selection string will be delivered as the
-{@code selection} string parameter, and the query entered into the search dialog will be delivered
-as the first element in the {@code selectionArgs} string array parameter.</p>
+{@code selection} and {@code selectionArgs} parameters to carry the appropriate values. In such a
+case, add the {@code android:searchSuggestSelection} attribute to your searchable configuration with
+your SQLite selection string. In the selection string, include a question mark ("?") as
+a placeholder for the actual search query. The Search Manager calls {@link
+android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} with the
+selection string as the {@code selection} parameter and the search query as the first
+element in the {@code selectionArgs} array.</p>
<p>For example, here's how you might form the {@code android:searchSuggestSelection} attribute to
create a full-text search statement:</p>
<pre>
-&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_label"
android:hint="@string/search_hint"
- android:searchSuggestAuthority="my.package.MyCustomSuggestionProvider"
+ android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider"
android:searchSuggestIntentAction="android.Intent.action.VIEW"
- android:searchSuggestSelection="word MATCH ?">
-&lt;/searchable>
+ <b>android:searchSuggestSelection="word MATCH ?"</b>&gt;
+&lt;/searchable&gt;
</pre>
-<p>When you then receive the {@code selection} and {@code selectionArgs} parameters in your {@link
-android.content.ContentProvider#query(Uri,String[],String,String[],String) ContentProvider.query()}
-method, they will carry the selection ("word MATCH ?") and the query text, respectively. When
-these are passed to an SQLite {@link
-android.database.sqlite.SQLiteDatabase#query(String,String[],String,String[],String,String,
-String) query} method, they will be synthesized together (replacing the question mark with the query
-text, wrapped in single-quotes). Note that if you chose this method and need to add any wildcards to
-your query text, you must do so by appending (and/or prefixing) them to the {@code selectionArgs}
-parameter, because this is the value that will be wrapped in quotes and inserted in place of the
+<p>With this configuration, your {@link
+android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method
+delivers the {@code selection} parameter as "word MATCH ?" and the {@code selectionArgs}
+parameter as whatever the user entered in the search dialog. When you pass these to an SQLite
+{@link android.database.sqlite.SQLiteDatabase#query(String,String[],String,String[],String,String,
+String) query()} method, as their respective arguments, they are synthesized together (the
+question mark is replaced with the query
+text). If you chose to receive suggestion queries this way and need to add wildcards to
+the query text, append (and/or prefix) them to the {@code selectionArgs}
+parameter, because this value is wrapped in quotes and inserted in place of the
question mark.</p>
+<p>Another new attribute in the example above is {@code android:searchSuggestIntentAction}, which
+defines the Intent action sent with each Intent when the user selects a suggestion. It is
+discussed further in the section about <a href="#IntentForSuggestions">Declaring an Intent for
+suggestions</a>.</p>
+
<p class="note"><strong>Tip:</strong> If you don't want to define a selection clause in
the {@code android:searchSuggestSelection} attribute, but would still like to receive the query
text in the {@code selectionArgs} parameter, simply provide a non-null value for the {@code
-android:searchSuggestSelection} attribute. This will trigger the query to be passed in {@code
+android:searchSuggestSelection} attribute. This triggers the query to be passed in {@code
selectionArgs} and you can ignore the {@code selection} parameter. In this way, you can instead
define the actual selection clause at a lower level so that your content provider doesn't have to
handle it.</p>
@@ -287,11 +314,12 @@ handle it.</p>
<div class="sidebox-wrapper">
<div class="sidebox">
-<h2>Creating a Cursor on the fly</h2>
-<p>If your search suggestions are not stored in a table format using the columns required by the
+<h2>Creating a Cursor without a table</h2>
+<p>If your search suggestions are not stored in a table format (such as an SQLite table) using the
+columns required by the
Search Manager, then you can search your suggestion data for matches and then format them
-into the necessary table on the fly. To do so, create a {@link android.database.MatrixCursor} using
-the required column names and then add a row for each suggestion using {@link
+into the necessary table on each request. To do so, create a {@link android.database.MatrixCursor}
+using the required column names and then add a row for each suggestion using {@link
android.database.MatrixCursor#addRow(Object[])}. Return the final product from your Content
Provider's {@link
android.content.ContentProvider#query(Uri,String[],String,String[],String) query()} method.</p>
@@ -299,97 +327,94 @@ android.content.ContentProvider#query(Uri,String[],String,String[],String) query
</div>
<p>When you return suggestions to the Search Manager with a {@link android.database.Cursor}, the
-Search Manager expects there to be specific columns in each row. So, regardless of whether you
+Search Manager expects specific columns in each row. So, regardless of whether you
decide to store
your suggestion data in an SQLite database on the device, a database on a web server, or another
format on the device or web, you must format the suggestions as rows in a table and
-present them with a {@link android.database.Cursor}. There are several columns that the Search
-Manager will understand, but only two are required:</p>
+present them with a {@link android.database.Cursor}. The Search
+Manager understands several columns, but only two are required:</p>
<dl>
<dt>{@link android.provider.BaseColumns#_ID}</dt>
- <dd>This is the unique row ID for each suggestion. The search dialog requires this in order
-to present the suggestions in a ListView.</dd>
+ <dd>A unique integer row ID for each suggestion. The search dialog requires this in order
+to present suggestions in a ListView.</dd>
<dt>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1}</dt>
- <dd>This is the line of text that will be presented to the user as a suggestion.</dd>
+ <dd>The string that is presented as a suggestion.</dd>
</dl>
-<p>The following columns are all optional (and most will be discussed further in the following
-sections, so you may want to skip this list for now):</p>
+<p>The following columns are all optional (and most are discussed further in the following
+sections):</p>
<dl>
<dt>{@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_2}</dt>
- <dd>If your Cursor includes this column, then all suggestions will be provided in a two-line
-format. The data in this column will be displayed as a second, smaller line of text below the
+ <dd>A string. If your Cursor includes this column, then all suggestions are provided in a
+two-line format. The string in this column is displayed as a second, smaller line of text below the
primary suggestion text. It can be null or empty to indicate no secondary text.</dd>
<dt>{@link android.app.SearchManager#SUGGEST_COLUMN_ICON_1}</dt>
- <dd>If your Cursor includes this column, then all suggestions will be provided in an
-icon-plus-text format with the icon on the left side. This value should be a reference to the
-icon. It can be null or zero to indicate no icon in this row.</dd>
+ <dd>A drawable resource, content, or file URI string. If your Cursor includes this column, then
+all suggestions are provided in an icon-plus-text format with the drawable icon on the left side.
+This can be null or zero to indicate no icon in this row.</dd>
<dt>{@link android.app.SearchManager#SUGGEST_COLUMN_ICON_2}</dt>
- <dd>If your Cursor includes this column, then all suggestions will be provided in an
-icon-plus-text format with the icon on the right side. This value should be a reference to the
-icon. It can be null or zero to indicate no icon in this row.</dd>
+ <dd>A drawable resource, content, or file URI string. If your Cursor includes this column, then
+all suggestions are provided in an icon-plus-text format with the icon on the right side. This can
+be null or zero to indicate no icon in this row.</dd>
<dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION}</dt>
- <dd>If this column exists and this element exists at the given row, this is the action that will
-be used when forming the suggestion's Intent . If the element is not provided, the action will be
-taken from the {@code android:searchSuggestIntentAction} field in your searchable configuration. At
-least one of these
-must be present for the suggestion to generate an Intent. Note: If your action is the same for all
+ <dd>An Intent action string. If this column exists and contains a value at the given row, the
+action defined here is used when forming the suggestion's Intent. If the element is not
+provided, the action is taken from the {@code android:searchSuggestIntentAction} field in your
+searchable configuration. If your action is the same for all
suggestions, it is more efficient to specify the action using {@code
-android:searchSuggestIntentAction} and omit this column from the Cursor .</dd>
+android:searchSuggestIntentAction} and omit this column.</dd>
<dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA}</dt>
- <dd>If this column exists and this element exists at the given row, this is the data that will be
-used when forming the suggestion's Intent. If the element is not provided, the data will be taken
-from the {@code android:searchSuggestIntentData} field in your searchable configuration. If neither
-source is provided,
-the Intent's data field will be null. Note: If your data is the same for all suggestions, or can be
+ <dd>A data URI string. If this column exists and contains a value at the given row, this is the
+data that is used when forming the suggestion's Intent. If the element is not provided, the data is
+taken from the {@code android:searchSuggestIntentData} field in your searchable configuration. If
+neither source is provided,
+the Intent's data field is null. If your data is the same for all suggestions, or can be
described using a constant part and a specific ID, it is more efficient to specify it using {@code
-android:searchSuggestIntentData} and omit this column from the Cursor .
+android:searchSuggestIntentData} and omit this column.
</dd>
<dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID}</dt>
- <dd>If this column exists and this element exists at the given row, then "/" and this value will
-be appended to the data field in the Intent. This should only be used if the data field specified
+ <dd>A URI path string. If this column exists and contains a value at the given row, then "/" and
+this value is appended to the data field in the Intent. This should only be used if the data field
+specified
by the {@code android:searchSuggestIntentData} attribute in the searchable configuration has already
been set to an appropriate base string.</dd>
<dt>{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_EXTRA_DATA}</dt>
- <dd>If this column exists and this element exists at a given row, this is the <em>extra</em> data
-that will be used when forming the suggestion's Intent. If not provided, the Intent's extra data
-field will be
-null. This column allows suggestions to provide additional arbitrary data which will be
+ <dd>Arbitrary data. If this column exists and contains a value at a given row, this is the
+<em>extra</em> data used when forming the suggestion's Intent. If not provided, the
+Intent's extra data field is null. This column allows suggestions to provide additional data that is
included as an extra in the Intent's {@link android.app.SearchManager#EXTRA_DATA_KEY} key.</dd>
<dt>{@link android.app.SearchManager#SUGGEST_COLUMN_QUERY}</dt>
- <dd>If this column exists and this element exists at the given row, this is the data that will be
+ <dd>If this column exists and this element exists at the given row, this is the data that is
used when forming the suggestion's query, included as an extra in the Intent's {@link
android.app.SearchManager#QUERY} key. Required if suggestion's action is {@link
android.content.Intent#ACTION_SEARCH}, optional otherwise.</dd>
<dt>{@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID}</dt>
- <dd>Only used when providing suggestions for Quick Search Box. This column is used to indicate
+ <dd>Only used when providing suggestions for Quick Search Box. This column indicates
whether a search suggestion should be stored as a
-shortcut, and whether it should be validated. Shortcuts are usually formed when the user clicks a
-suggestion from Quick Search Box. If missing, the result will be stored as a shortcut and never
-refreshed. If set to {@link android.app.SearchManager#SUGGEST_NEVER_MAKE_SHORTCUT}, the result will
-not be stored as a shortcut.
-Otherwise, the shortcut id will be used to check back for for an up to date suggestion using
+shortcut and whether it should be validated. Shortcuts are usually formed when the user clicks a
+suggestion from Quick Search Box. If missing, the result is stored as a shortcut and never
+refreshed. If set to {@link android.app.SearchManager#SUGGEST_NEVER_MAKE_SHORTCUT}, the result is
+not stored as a shortcut.
+Otherwise, the shortcut ID is used to check back for an up to date suggestion using
{@link android.app.SearchManager#SUGGEST_URI_PATH_SHORTCUT}.</dd>
<dt>{@link android.app.SearchManager#SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING}</dt>
- <dd>Only used when providing suggestions for Quick Search Box. This column is used to specify that
+ <dd>Only used when providing suggestions for Quick Search Box. This column specifies that
a spinner should be shown instead of an icon from {@link
android.app.SearchManager#SUGGEST_COLUMN_ICON_2}
while the shortcut of this suggestion is being refreshed in Quick Search Box.</dd>
</dl>
-<p>Again, most of these columns will be discussed in the relevant sections below, so don't worry if
-they don't make sense to you now.</p>
+<p>Some of these columns are discussed more in the following sections.</p>
<h2 id="IntentForSuggestions">Declaring an Intent for suggestions</h2>
-<p>When the user selects a suggestion from the list that appears below the search
-dialog (instead of performing a search), the Search Manager will send
-a custom {@link android.content.Intent} to your searchable Activity. You must define both the
-<em>action</em> and <em>data</em> for the Intent.</p>
+<p>When the user selects a suggestion from the list that appears below the search dialog, the Search
+Manager sends a custom {@link android.content.Intent} to your searchable Activity. You must define
+the action and data for the Intent.</p>
<h3 id="IntentAction">Declaring the Intent action</h3>
@@ -397,34 +422,42 @@ a custom {@link android.content.Intent} to your searchable Activity. You must de
<p>The most common Intent action for a custom suggestion is {@link
android.content.Intent#ACTION_VIEW}, which is appropriate when
you want to open something, like the definition for a word, a person's contact information, or a web
-page. However, the Intent action can be whatever you want and can even be different for each
+page. However, the Intent action can be any other action and can even be different for each
suggestion.</p>
-<p>To declare an Intent action that will be the same for all suggestions, define the action in
-the {@code android:searchSuggestIntentAction} attribute of your searchable configuration file. For
-example:</p>
+<p>Depending on whether you want all suggestions to use the same Intent action, you
+can define the action in two ways:</p>
+
+<ol type="a">
+ <li>Use the {@code android:searchSuggestIntentAction} attribute of your searchable configuration
+file to define the action for all suggestions. <p>For example:</p>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_label"
android:hint="@string/search_hint"
- android:searchSuggestAuthority="my.package.MySuggestionProvider"
- android:searchSuggestIntentAction="android.Intent.action.VIEW" >
+ android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider"
+ <b>android:searchSuggestIntentAction="android.Intent.action.VIEW"</b> >
&lt;/searchable>
</pre>
-<p>If you want to declare an Intent action that's unique for each suggestion, add the {@link
-android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column to
+ </li>
+ <li>Use the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column to define the
+action for individual suggestions.
+ <p>Add the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column to
your suggestions table and, for each suggestion, place in it the action to use (such as
-{@code "android.Intent.action.VIEW"}). </p>
+{@code "android.Intent.action.VIEW"}).</p>
+
+ </li>
+</ol>
<p>You can also combine these two techniques. For instance, you can include the {@code
android:searchSuggestIntentAction} attribute with an action to be used with all suggestions by
default, then override this action for some suggestions by declaring a different action in the
{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column. If you do not include
a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column, then the
-Intent provided in the {@code android:searchSuggestIntentAction} attribute will be used.</p>
+Intent provided in the {@code android:searchSuggestIntentAction} attribute is used.</p>
<p class="note"><strong>Note</strong>: If you do not include the
{@code android:searchSuggestIntentAction} attribute in your searchable configuration, then you
@@ -432,45 +465,45 @@ Intent provided in the {@code android:searchSuggestIntentAction} attribute will
column for every suggestion, or the Intent will fail.</p>
+
<h3 id="IntentData">Declaring Intent data</h3>
-<p>When the user selects a suggestion, your searchable Activity will receive the Intent with the
+<p>When the user selects a suggestion, your searchable Activity receives the Intent with the
action you've defined (as discussed in the previous section), but the Intent must also carry
-data in order for your Activity to identify which suggestions was selected. Specifically,
+data in order for your Activity to identify which suggestion was selected. Specifically,
the data should be something unique for each suggestion, such as the row ID for the suggestion in
-your suggestions table. When the Intent is received,
+your SQLite table. When the Intent is received,
you can retrieve the attached data with {@link android.content.Intent#getData()} or {@link
android.content.Intent#getDataString()}.</p>
-<p>There are two ways to define the data that is included with the Intent:</p>
+<p>You can define the data included with the Intent in two ways:</p>
<ol type="a">
<li>Define the data for each suggestion inside the {@link
-android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column of your suggestions table.</li>
- <li>Fragment a data URI into two pieces: the portion common to all suggestions and the portion
-unique to each suggestion. Place these parts into the {@code android:searchSuggestIntentData}
-attribute of the searchable configuration and the {@link
-android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column of your
-suggestions table, respectively.</li>
-</ol>
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column of your suggestions table.
-<p>The first option is straight-forward. Simply provide all necessary data information for each
-Intent in the suggestions table by including the {@link
-android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column and then populating it with unique
-data for each row. The data from this column will be attached to the Intent exactly as it
-is found in this column. You can then retrieve it with with {@link android.content.Intent#getData()}
-or {@link android.content.Intent#getDataString()}.</p>
+<p>Provide all necessary data information for each Intent in the suggestions table by including the
+{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column and then populating it with
+unique data for each row. The data from this column is attached to the Intent exactly as you
+define it in this column. You can then retrieve it with with {@link
+android.content.Intent#getData()} or {@link android.content.Intent#getDataString()}.</p>
<p class="note"><strong>Tip</strong>: It's usually easiest to use the table's row ID as the
-Intent data because it's always unique. And the easiest way to do that is by using the
+Intent data, because it's always unique. And the easiest way to do that is by using the
{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column name as an alias for the row ID
column. See the <a
href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary sample
-app</a> for an example in which {@link android.database.sqlite.SQLiteQueryBuilder} is used to
-create a projection map of column names to aliases.</p>
+app</a> for an example in which {@link android.database.sqlite.SQLiteQueryBuilder} creates a
+projection map of column names to aliases.</p>
+ </li>
+
+ <li>Fragment a data URI into two pieces: the portion common to all suggestions and the portion
+unique to each suggestion. Place these parts into the {@code android:searchSuggestIntentData}
+attribute of the searchable configuration and the {@link
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column of your
+suggestions table, respectively.
-<p>The second option is to fragment your data URI into the common piece and the unique piece.
-Declare the piece of the URI that is common to all suggestions in the {@code
+<p>Declare the piece of the URI that is common to all suggestions in the {@code
android:searchSuggestIntentData} attribute of your searchable configuration. For example:</p>
<pre>
@@ -478,35 +511,40 @@ android:searchSuggestIntentData} attribute of your searchable configuration. For
&lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_label"
android:hint="@string/search_hint"
- android:searchSuggestAuthority="my.package.MySuggestionProvider"
+ android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider"
android:searchSuggestIntentAction="android.Intent.action.VIEW"
- android:searchSuggestIntentData="content://my.package/datatable" >
+ <b>android:searchSuggestIntentData="content://com.example/datatable"</b> >
&lt;/searchable>
</pre>
-<p>Now include the final path for each suggestion (the unique part) in the {@link
+<p>Then include the final path for each suggestion (the unique part) in the {@link
android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID}
-column of your suggestions table. When the user selects a suggestion, the Search Manager will take
-the string from {@code android:searchSuggestIntentData}, append a slash ("/") and then add the
+column of your suggestions table. When the user selects a suggestion, the Search Manager takes
+the string from {@code android:searchSuggestIntentData}, appends a slash ("/") and then adds the
respective value from the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column to
form a complete content URI. You can then retrieve the {@link android.net.Uri} with with {@link
android.content.Intent#getData()}.</p>
+ </li>
+</ol>
+
<h4>Add more data</h4>
<p>If you need to express even more information with your Intent, you can add another table column,
{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_EXTRA_DATA}, which can store additional
-information about the suggestion. The data saved in this column will be placed in {@link
+information about the suggestion. The data saved in this column is placed in {@link
android.app.SearchManager#EXTRA_DATA_KEY} of the Intent's extra Bundle.</p>
+
<h2 id="HandlingIntent">Handling the Intent</h2>
-<p>Now that your search dialog provides custom search suggestions with custom formatted Intents, you
-need your searchable Activity to handle these Intents as they are delivered once the user selects a
-suggestion. (This is, of course, in addition to handling the {@link
-android.content.Intent#ACTION_SEARCH} Intent, which your searchable Activity already does.)
-Accepting the new Intent is rather self-explanatory, so we'll skip straight to an example:</p>
+<p>Now that your search dialog provides custom search suggestions with custom Intents, you
+need your searchable Activity to handle these Intents when the user selects a
+suggestion. This is in addition to handling the {@link
+android.content.Intent#ACTION_SEARCH} Intent, which your searchable Activity already does.
+Here's an example of how you can handle the Intents during your Activity {@link
+android.app.Activity#onCreate(Bundle) onCreate()} callback:</p>
<pre>
Intent intent = getIntent();
@@ -515,26 +553,32 @@ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
doSearch(query);
} else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
- // Handle a suggestions click (because my suggestions all use ACTION_VIEW)
- Uri data = intent.getData());
- showResult(rowId);
+ // Handle a suggestions click (because the suggestions all use ACTION_VIEW)
+ Uri data = intent.getData();
+ showResult(data);
}
</pre>
<p>In this example, the Intent action is {@link
android.content.Intent#ACTION_VIEW} and the data carries a complete URI pointing to the suggested
item, as synthesized by the {@code android:searchSuggestIntentData} string and {@link
-android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column. The URI is then passed to a local
-method that will query the content provider for the item specified by the URI and show it.</p>
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA_ID} column. The URI is then passed to the local
+{@code showResult()} method that queries the content provider for the item specified by the URI.</p>
+<p class="note"><strong>Note:</strong> You do <em>not</em> need to add an Intent filter to your
+Android manifest file for the Intent action you defined with the {@code
+android:searchSuggestIntentAction} attribute or {@link
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column. The Search Manager opens your
+searchable Activity by name to deliver the suggestion's Intent, so the Activity does not need to
+declare the accepted action.</p>
<h2 id="RewritingQueryText">Rewriting the query text</h2>
-<p>If the user navigates through the suggestions list using the device directional controls, the
-text in the search dialog won't change, by default. However, you can temporarily rewrite the
-user's query text as it appears in the text box with
-a query that matches the currently selected suggestion. This enables the user to see what query is
+<p>If the user navigates through the suggestions list using the directional controls (trackball or
+d-pad), the text in the search dialog won't change, by default. However, you can temporarily rewrite
+the user's query text as it appears in the text box with
+a query that matches the suggestion currently in focus. This enables the user to see what query is
being suggested (if appropriate) and then select the search box and edit the query before
dispatching it as a search.</p>
@@ -544,39 +588,40 @@ dispatching it as a search.</p>
<li>Add the {@code android:searchMode} attribute to your searchable configuration with the
"queryRewriteFromText" value. In this case, the content from the suggestion's {@link
android.app.SearchManager#SUGGEST_COLUMN_TEXT_1}
-column will be used to rewrite the query text.</li>
+column is used to rewrite the query text.</li>
<li>Add the {@code android:searchMode} attribute to your searchable configuration with the
"queryRewriteFromData" value. In this case, the content from the suggestion's
-{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column will be used to rewrite the
-query text. Note that this should only
-be used with Uri's or other data formats that are intended to be user-visible, such as HTTP URLs.
-Internal Uri schemes should not be used to rewrite the query in this way.</li>
+{@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column is used to rewrite the
+query text. This should only
+be used with URI's or other data formats that are intended to be user-visible, such as HTTP URLs.
+Internal URI schemes should not be used to rewrite the query in this way.</li>
<li>Provide a unique query text string in the {@link
android.app.SearchManager#SUGGEST_COLUMN_QUERY} column of your suggestions table. If this column is
-present and contains a value for the current suggestion, it will be used to rewrite the query text
+present and contains a value for the current suggestion, it is used to rewrite the query text
(and override either of the previous implementations).</li>
</ol>
+
<h2 id="QSB">Exposing search suggestions to Quick Search Box</h2>
-<p>Once your application is configured to provide custom search suggestions, making them available
-to the globally-accessible Quick Search Box is as easy as modifying your searchable configuration to
+<p>Once you configure your application to provide custom search suggestions, making them available
+to the globally accessible Quick Search Box is as easy as modifying your searchable configuration to
include {@code android:includeInGlobalSearch} as "true".</p>
-<p>The only scenario in which additional work will be required is if your content provider for
-custom suggestions requires a permission for read access. In which case, you need to add a special
-{@code &lt;path-permission>} element for the provider to grant Quick Search Box read access to your
-content provider. For example:</p>
+<p>The only scenario in which additional work is necessary is when your content provider demands a
+read permission. In which case, you need to add a special
+{@code &lt;path-permission&gt;} element for the provider to grant Quick Search Box read access to
+your content provider. For example:</p>
<pre>
&lt;provider android:name="MySuggestionProvider"
- android:authorities="my.package.authority"
+ android:authorities="com.example.MyCustomSuggestionProvider"
android:readPermission="com.example.provider.READ_MY_DATA"
- android:writePermission="com.example.provider.WRITE_MY_DATA">
+ android:writePermission="com.example.provider.WRITE_MY_DATA"&gt;
&lt;path-permission android:pathPrefix="/search_suggest_query"
- android:readPermission="android.permission.GLOBAL_SEARCH" />
-&lt;/provider>
+ android:readPermission="android.permission.GLOBAL_SEARCH" /&gt;
+&lt;/provider&gt;
</pre>
<p>In this example, the provider restricts read and write access to the content. The
@@ -585,8 +630,8 @@ inside the {@code "/search_suggest_query"} path prefix when the {@code
"android.permission.GLOBAL_SEARCH"} permission exists. This grants access to Quick Search Box
so that it may query your content provider for suggestions.</p>
-<p>Content providers that enforce no permissions are already available to the search
-infrastructure.</p>
+<p>If your content provider does not enforce read permissions, then Quick Search Box can read
+it by default.</p>
<h3 id="EnablingSuggestions">Enabling suggestions on a device</h3>
@@ -608,89 +653,93 @@ android:searchSettingsDescription} attribute to your searchable configuration. F
&lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_label"
android:hint="@string/search_hint"
- android:searchSuggestAuthority="my.package.MySuggestionProvider"
+ android:searchSuggestAuthority="com.example.MyCustomSuggestionProvider"
android:searchSuggestIntentAction="android.Intent.action.VIEW"
android:includeInGlobalSearch="true"
- android:searchSettingsDescription="@string/search_description" >
+ <b>android:searchSettingsDescription="@string/search_description"</b> >
&lt;/searchable>
</pre>
<p>The string for {@code android:searchSettingsDescription} should be as concise as possible and
state the content that is searchable. For example, "Artists, albums, and tracks" for a music
application, or "Saved notes" for a notepad application. Providing this description is important so
-the user knows what kind of suggestions will be provided. This attribute should always be included
+the user knows what kind of suggestions are provided. You should always include this attribute
when {@code android:includeInGlobalSearch} is "true".</p>
-<p>Remember that the user must visit this settings menu to enable search suggestions for your
-application before your search suggestions will appear in Quick Search Box. As such, if search is an
-important aspect of your application, then you may want to consider a way to message this to your
-users &mdash; perhaps with a note the first time they launch the app about how to enable search
-suggestions for Quick Search Box.</p>
+<p>Remember that the user must visit the settings menu to enable search suggestions for your
+application before your search suggestions appear in Quick Search Box. As such, if search is an
+important aspect of your application, then you might want to consider a way to convey that to
+your users &mdash; you might provide a note the first time they launch the app that instructs
+them how to enable search suggestions for Quick Search Box.</p>
<h3 id="ManagingShortcuts">Managing Quick Search Box suggestion shortcuts</h3>
-<p>Suggestions that the user selects from Quick Search Box may be automatically made into shortcuts.
+<p>Suggestions that the user selects from Quick Search Box can be automatically made into shortcuts.
These are suggestions that the Search Manager has copied from your content provider so it can
quickly access the suggestion without the need to re-query your content provider. </p>
<p>By default, this is enabled for all suggestions retrieved by Quick Search Box, but if your
-suggestion data may change over time, then you can request that the shortcuts be refreshed. For
+suggestion data changes over time, then you can request that the shortcuts be refreshed. For
instance, if your suggestions refer to dynamic data, such as a contact's presence status, then you
should request that the suggestion shortcuts be refreshed when shown to the user. To do so,
include the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} in your suggestions table.
Using this column, you can
-configure the shortcut behavior for each suggestion in the following ways:</p>
+configure the shortcut behavior for each suggestion in one of the following ways:</p>
<ol type="a">
- <li>Have Quick Search Box re-query your content provider for a fresh version of the shortcutted
-suggestion.
+ <li>Have Quick Search Box re-query your content provider for a fresh version of the suggestion
+shortcut.
<p>Provide a value in the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column
-and the suggestion will be
-re-queried for a fresh version of the suggestion each time the shortcut is displayed. The shortcut
-will be quickly displayed with whatever data was most recently available until the refresh query
-returns, after which the suggestion will be dynamically refreshed with the new information. The
-refresh query will be sent to your content provider with a URI path of {@link
+and the suggestion is
+re-queried for a fresh version each time the shortcut is displayed. The shortcut
+is quickly displayed with whatever data was most recently available until the refresh query
+returns, at which point the suggestion is refreshed with the new information. The
+refresh query is sent to your content provider with a URI path of {@link
android.app.SearchManager#SUGGEST_URI_PATH_SHORTCUT}
-(instead of {@link android.app.SearchManager#SUGGEST_URI_PATH_QUERY}). The Cursor you return should
-contain one suggestion using the
+(instead of {@link android.app.SearchManager#SUGGEST_URI_PATH_QUERY}).</p>
+ <p>The {@link android.database.Cursor} you return should contain one suggestion using the
same columns as the original suggestion, or be empty, indicating that the shortcut is no
-longer valid (in which case, the suggestion will disappear and the shortcut will be removed).</p>
- <p>If a suggestion refers to data that could take longer to refresh, such as a network based
-refresh, you may also add the {@link
+longer valid (in which case, the suggestion disappears and the shortcut is removed).</p>
+ <p>If a suggestion refers to data that could take longer to refresh, such as a network-based
+refresh, you can also add the {@link
android.app.SearchManager#SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING} column to your suggestions
table with a value
of "true" in order to show a progress spinner for the right hand icon until the refresh is complete.
-(Any value other than "true" will not show the progress spinner.)</p></li>
+Any value other than "true" does not show the progress spinner.</p>
+ </li>
+
<li>Prevent the suggestion from being copied into a shortcut at all.
<p>Provide a value of {@link android.app.SearchManager#SUGGEST_NEVER_MAKE_SHORTCUT} in the
{@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column. In
-this case, the suggestion will never be copied into a shortcut. This should only be necessary if you
-absolutely do not want the previously copied suggestion to appear at all. (Recall that if you
-provide a normal value for the column then the suggestion shortcut will appear only until the
+this case, the suggestion is never copied into a shortcut. This should only be necessary if you
+absolutely do not want the previously copied suggestion to appear. (Recall that if you
+provide a normal value for the column, then the suggestion shortcut appears only until the
refresh query returns.)</p></li>
<li>Allow the default shortcut behavior to apply.
- <p>Simply leave the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} empty for each
+ <p>Leave the {@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} empty for each
suggestion that will not change and can be saved as a shortcut.</p></li>
</ol>
-<p>Of course, if none of your suggestions will ever change, then you do not need the
+<p>If none of your suggestions ever change, then you do not need the
{@link android.app.SearchManager#SUGGEST_COLUMN_SHORTCUT_ID} column at all.</p>
-<p class="note"><strong>Note</strong>: Quick Search Box will ultimately decide whether to shortcut
-your app's suggestions, considering these values as a strong request from your application.</p>
+<p class="note"><strong>Note</strong>: Quick Search Box ultimately decides whether or not to create
+a shortcut for a suggestion, considering these values as a strong request from your
+application&mdash;there is no guarantee that the behavior you have requested for your suggestion
+shortcuts will be honored.</p>
<h3 id="AboutRanking">About Quick Search Box suggestion ranking</h3>
-<p>Once your application's search results are made available to Quick Search Box, how they surface
-to the user for a particular query will be determined as appropriate by Quick Search Box ranking.
-This may depend on how many other apps have results for that query, and how often the user has
-selected on your results compared to those of the other apps. There is no guarantee about how
-ranking will occur, or whether your app's suggestions will show at all for a given query. In
-general, you can expect that providing quality results will increase the likelihood that your app's
-suggestions are provided in a prominent position, and apps that provide lower quality suggestions
-will be more likely to be ranked lower and/or not displayed.</p>
+<p>Once you make your application's search suggestions available to Quick Search Box, the Quick
+Search Box ranking determines how the suggestions are surfaced to the user for a particular query.
+This might depend on how many other apps have results for that query, and how often the user has
+selected your results compared to those from other apps. There is no guarantee about how your
+suggestions are ranked, or whether your app's suggestions show at all for a given query. In
+general, you can expect that providing quality results increases the likelihood that your app's
+suggestions are provided in a prominent position and apps that provide low quality suggestions
+are more likely to be ranked lower or not displayed.</p>
<div class="special">
<p>See the <a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
diff --git a/docs/html/guide/topics/search/adding-recent-query-suggestions.jd b/docs/html/guide/topics/search/adding-recent-query-suggestions.jd
index 37e0e82..cb063a1 100644
--- a/docs/html/guide/topics/search/adding-recent-query-suggestions.jd
+++ b/docs/html/guide/topics/search/adding-recent-query-suggestions.jd
@@ -5,20 +5,22 @@ parent.link=index.html
<div id="qv-wrapper">
<div id="qv">
-<h2>Key classes</h2>
-<ol>
-<li>{@link android.provider.SearchRecentSuggestions}</li>
-<li>{@link android.content.SearchRecentSuggestionsProvider}</li>
-</ol>
<h2>In this document</h2>
<ol>
<li><a href="#TheBasics">The Basics</a></li>
-<li><a href="#RecentQuerySearchableConfiguration">Modifying the searchable
-configuration</a></li>
<li><a href="#RecentQueryContentProvider">Creating a Content Provider</a></li>
-<li><a href="#SavingQueries">Saving queries</a></li>
-<li><a href="#ClearingSuggestionData">Clearing the suggestion data</a></li>
+<li><a href="#RecentQuerySearchableConfiguration">Modifying the Searchable
+Configuration</a></li>
+<li><a href="#SavingQueries">Saving Queries</a></li>
+<li><a href="#ClearingSuggestionData">Clearing the Suggestion Data</a></li>
</ol>
+
+<h2>Key classes</h2>
+<ol>
+<li>{@link android.provider.SearchRecentSuggestions}</li>
+<li>{@link android.content.SearchRecentSuggestionsProvider}</li>
+</ol>
+
<h2>See also</h2>
<ol>
<li><a href="searchable-config.html">Searchable Configuration</a></li>
@@ -26,99 +28,76 @@ configuration</a></li>
</div>
</div>
-<p>The Android search framework provides the ability for your application to
-provide suggestions while the user types into the Android search dialog. In this guide, you'll learn
-how to create recent query suggestions. These are suggestions based
-on queries previously entered by the user. So, if the user previously searched for "puppies" then it
-will appear as a suggestion as they begin typing the same string of text. The screenshot below
-shows an example of recent query suggestions.</p>
+<p>When using the Android search dialog, you can provide search suggestions based on recent search
+queries. For example, if a user previously searched for "puppies," then that query appears as a
+suggestion once he or she begins typing the same query. Figure 1 shows an example of a search dialog
+with recent query suggestions.</p>
+
+<p>Before you begin, you need to implement the search dialog for basic searches in your application.
+If you haven't, see <a href="search-dialog.html">Using the Android Search Dialog</a>.</p>
-<p>Before you begin, you need to have implemented the Android search dialog for searches in your
-application. If you haven't done this, see <a href="search-dialog.html">Using the Android Search
-Dialog</a>.</p>
<h2 id="TheBasics">The Basics</h2>
-<img src="{@docRoot}images/search/search-suggest-recent-queries.png" alt="" height="417"
-style="float:right;clear:right;" />
+<div class="figure" style="width:250px">
+<img src="{@docRoot}images/search/search-suggest-recent-queries.png" alt="" height="417" />
+<p class="img-caption"><strong>Figure 1.</strong> Screenshot of a search dialog with recent query
+suggestions.</p>
+</div>
<p>Recent query suggestions are simply saved searches. When the user selects one of
-the suggestions, your searchable Activity will receive a normal {@link
+the suggestions, your searchable Activity receives a {@link
android.content.Intent#ACTION_SEARCH} Intent with the suggestion as the search query, which your
-searchable Activity will already handle.</p>
+searchable Activity already handles (as described in <a href="search-dialog.html">Using the Android
+Search Dialog</a>).</p>
<p>To provide recent queries suggestions, you need to:</p>
<ul>
- <li>Implement a basic searchable Activity, as documented in <a
-href="{@docRoot}guide/topics/search/search-dialog.html">Using the Android Search Dialog</a>.</li>
+ <li>Implement a searchable Activity, <a
+href="{@docRoot}guide/topics/search/search-dialog.html">using the Android Search Dialog</a>.</li>
<li>Create a content provider that extends {@link
android.content.SearchRecentSuggestionsProvider} and declare it in your application manifest.</li>
- <li>Modify the searchable configuration with information about the content provider.</li>
- <li>Save queries to your content provider each time a search is made.</li>
+ <li>Modify the searchable configuration with information about the content provider that
+provides search suggestions.</li>
+ <li>Save queries to your content provider each time a search is executed.</li>
</ul>
-<p>Just like the Search Manager handles the rendering of the search dialog, it will also do the work
-to display all search suggestions below the search dialog. All you need to do is provide a source
-from which the suggestions can be retrieved.</p>
+<p>Just as the Search Manager displays the search dialog, it also displays the
+search suggestions. All you need to do is provide a source from which the suggestions can be
+retrieved.</p>
-<p>When the Search Manager identifies that your Activity is searchable and also provides search
-suggestions, the following procedure will take place as soon as the user types into the Android
-search box:</p>
+<p>When the Search Manager identifies that your Activity is searchable and provides search
+suggestions, the following procedure takes place as soon as the user types into the search
+dialog:</p>
-<ul>
- <li>The Search Manager takes the search query text (whatever has been typed so far) and performs a
-query to the content provider that manages your suggestions.</li>
- <li>Your content provider then returns a {@link android.database.Cursor} that points to all
-suggestions that are relevant to the search query text.</li>
- <li>The Search Manager then displays the list of suggestions provided by the Cursor (as
-demonstrated in the screenshot to the right).</li>
-</ul>
+<ol>
+ <li>Search Manager takes the search query text (whatever has been typed so far) and performs a
+query to the content provider that contains your suggestions.</li>
+ <li>Your content provider returns a {@link android.database.Cursor} that points to all
+suggestions that match the search query text.</li>
+ <li>Search Manager displays the list of suggestions provided by the Cursor.</li>
+</ol>
-<p>At this point, the following may happen:</p>
+<p>Once the recent query suggestions are displayed, the following might happen:</p>
<ul>
- <li>If the user types another key, or changes the query in any way, the above steps are repeated
-and the suggestion list is updated as appropriate.</li>
+ <li>If the user types another key, or changes the query in any way, the aforementioned steps are
+repeated and the suggestion list is updated.</li>
<li>If the user executes the search, the suggestions are ignored and the search is delivered
to your searchable Activity using the normal {@link android.content.Intent#ACTION_SEARCH}
Intent.</li>
- <li>If the user selects a suggestion, a normal
-{@link android.content.Intent#ACTION_SEARCH} Intent is triggered, using the suggested text as the
-query.</li>
+ <li>If the user selects a suggestion, an
+{@link android.content.Intent#ACTION_SEARCH} Intent is delivered to your searchable Activity using
+the suggested text as the query.</li>
</ul>
-<p>As you'll soon discover, the {@link android.content.SearchRecentSuggestionsProvider} class that
-you'll extend for your content provider will automatically do the work described above, so there's
+<p>The {@link android.content.SearchRecentSuggestionsProvider} class that
+you extend for your content provider automatically does the work described above, so there's
actually very little code to write.</p>
-<h2 id="RecentQuerySearchableConfiguration">Modifying the searchable configuration</h2>
-
-<p>First, you need to add the {@code android:searchSuggestAuthority} and
-{@code android:searchSuggestSelection} attributes to the {@code &lt;searchable&gt;} element in your
-searchable configuration file. For example:</p>
-
-<pre>
-&lt;?xml version="1.0" encoding="utf-8"?>
-&lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
- android:label="@string/app_label"
- android:hint="@string/search_hint"
- android:searchSuggestAuthority="my.package.MySuggestionProvider"
- android:searchSuggestSelection=" ?" >
-&lt;/searchable>
-</pre>
-
-<p>The value for {@code android:searchSuggestAuthority} should be a fully-qualified name for
-your content provider: your application package name followed by the name of your content provider.
-This string must match the authority used in the content provider (discussed in the next section).
-</p>
-
-<p>The value for {@code android:searchSuggestSelection} must be a single question-mark, preceded by
-a space (" ?"), which is simply a placeholder for the SQLite selection argument (which will be
-automatically replaced by the query text entered by the user).</p>
-
<h2 id="RecentQueryContentProvider">Creating a Content Provider</h2>
@@ -131,7 +110,7 @@ suggestions:</p>
<pre>
public class MySuggestionProvider extends SearchRecentSuggestionsProvider {
- public final static String AUTHORITY = "my.package.MySuggestionProvider";
+ public final static String AUTHORITY = "com.example.MySuggestionProvider";
public final static int MODE = DATABASE_MODE_QUERIES;
public MySuggestionProvider() {
@@ -140,41 +119,72 @@ public class MySuggestionProvider extends SearchRecentSuggestionsProvider {
}
</pre>
-<p>The call to {@link android.content.SearchRecentSuggestionsProvider#setupSuggestions(String,int)}
-passes the name of the search authority (matching the one in the searchable configuration) and a
-database mode. The database mode must include {@link
+<p>The call to {@link android.content.SearchRecentSuggestionsProvider#setupSuggestions(String,int)
+setupSuggestions()} passes the name of the search authority and a
+database mode. The search authority can be any unique string, but the best practice is to use a
+fully qualified name for your content provider
+(package name followed by the provider's class name; for example,
+"com.example.MySuggestionProvider"). The database mode must include {@link
android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_QUERIES} and can optionally include
{@link
-android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_2LINES}, which will add another column
+android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_2LINES}, which adds another column
to the suggestions table that allows you to provide a second line of text with each suggestion. For
-example:</p>
+example, if you want to provide two lines in each suggestion:</p>
+
<pre>
public final static int MODE = DATABASE_MODE_QUERIES | DATABASE_MODE_2LINES;
</pre>
-<p>In the following section, you'll see how to save both lines of text.</p>
-
-<p>Now simply declare the content provider in your application manifest with the same authority
-string used in the class (and in the searchable configuration). For example:</p>
+<p>Now declare the content provider in your application manifest with the same authority
+string used in your {@link android.content.SearchRecentSuggestionsProvider} class (and in the
+searchable configuration). For example:</p>
<pre>
&lt;application>
&lt;provider android:name=".MySuggestionProvider"
- android:authorities="my.package.authority" />
+ android:authorities="com.example.MySuggestionProvider" />
...
&lt;/application>
</pre>
-<h2 id="SavingQueries">Saving queries</h2>
-<p>In order to populate your collection of recent queries, you need to add each query
-received by your searchable Activity to the content provider you've just built. To do this, create
-an instance of {@link
+<h2 id="RecentQuerySearchableConfiguration">Modifying the Searchable Configuration</h2>
+
+<p>To configure your search dialog to use your suggestions provider, you need to add
+the {@code android:searchSuggestAuthority} and {@code android:searchSuggestSelection} attributes to
+the {@code &lt;searchable&gt;} element in your searchable configuration file. For example:</p>
+
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/app_label"
+ android:hint="@string/search_hint"
+ <b>android:searchSuggestAuthority="com.example.MySuggestionProvider"
+ android:searchSuggestSelection=" ?"</b> >
+&lt;/searchable>
+</pre>
+
+<p>The value for {@code android:searchSuggestAuthority} should be a fully qualified name for
+your content provider that exactly matches the authority used in the content provider (the {@code
+AUTHORITY} string in the above example).
+</p>
+
+<p>The value for {@code android:searchSuggestSelection} must be a single question mark, preceded by
+a space ({@code " ?"}), which is simply a placeholder for the SQLite selection argument (which is
+automatically replaced by the query text entered by the user).</p>
+
+
+
+<h2 id="SavingQueries">Saving Queries</h2>
+
+<p>To populate your collection of recent queries, add each query
+received by your searchable Activity to your {@link
+android.content.SearchRecentSuggestionsProvider}. To do this, create an instance of {@link
android.provider.SearchRecentSuggestions} and call {@link
-android.provider.SearchRecentSuggestions#saveRecentQuery(String,String)} each time your searchable
-Activity receives a query. For example, here's how you can save the query during your
-Activity's {@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
+android.provider.SearchRecentSuggestions#saveRecentQuery(String,String) saveRecentQuery()} each time
+your searchable Activity receives a query. For example, here's how you can save the query during
+your Activity's {@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
<pre>
&#64;Override
@@ -193,24 +203,24 @@ public void onCreate(Bundle savedInstanceState) {
}
</pre>
-<p>Notice that the {@link android.content.SearchRecentSuggestionsProvider} constructor requires the
+<p>The {@link android.content.SearchRecentSuggestionsProvider} constructor requires the
same authority and database mode declared by your content provider.</p>
-<p>The {@link android.provider.SearchRecentSuggestions#saveRecentQuery(String,String)} method takes
+<p>The {@link android.provider.SearchRecentSuggestions#saveRecentQuery(String,String)
+saveRecentQuery()} method takes
the search query string as the first parameter and, optionally, a second string to include as the
-second line of the suggestion. The second parameter is only used if you've enabled two-line mode
-for the search suggestions with {@link
+second line of the suggestion (or null). The second parameter is only used if you've enabled
+two-line mode for the search suggestions with {@link
android.content.SearchRecentSuggestionsProvider#DATABASE_MODE_2LINES}. If you have enabled
-two-line mode, then the query text will be matched against this second line as well.</p>
+two-line mode, then the query text is also matched against this second line when the Search Manager
+looks for matching suggestions.</p>
-<p>That's all that's needed to build a recent queries suggestion provider. However, there's one
-other important thing to do: provide the ability for the user to clear this search history.</p>
-<h2 id="ClearingSuggestionData">Clearing the suggestion data</h2>
+<h2 id="ClearingSuggestionData">Clearing the Suggestion Data</h2>
<p>To protect the user's privacy, you should always provide a way for the user to clear the recent
-query suggestions. To clear the recent queries, simply call {@link
+query suggestions. To clear the query history, call {@link
android.provider.SearchRecentSuggestions#clearHistory()}. For example:</p>
<pre>
@@ -219,7 +229,7 @@ SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this,
suggestions.clearHistory();
</pre>
-<p>Simply execute this from your choice of a "Clear Search History" menu item,
-preference item, or button. You should also provide a confirmation dialog when this is pressed, to
+<p>Execute this from your choice of a "Clear Search History" menu item,
+preference item, or button. You should also provide a confirmation dialog to
verify that the user wants to delete their search history.</p>
diff --git a/docs/html/guide/topics/search/index.jd b/docs/html/guide/topics/search/index.jd
index b2252bb..78e0be2 100644
--- a/docs/html/guide/topics/search/index.jd
+++ b/docs/html/guide/topics/search/index.jd
@@ -13,98 +13,96 @@ page.title=Search
<ol>
<li><a href="searchable-config.html">Searchable Configuration</a></li>
</ol>
-<h2>See also</h2>
+<h2>Related Samples</h2>
<ol>
<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
-Dictionary sample app</a></li>
+Dictionary</a></li>
</ol>
</div>
</div>
-<p>The ability to search is considered to be a core user feature on Android. The user should be able
-to search any data that is available to them, whether the content is located on the device or the
-Internet. This experience should be seamless and consistent across the entire
-system, which is why Android provides a simple search framework to help you provide users with
+<p>Search is a core user feature on Android. Users should be able
+to search any data that is available to them, whether the content is located on the device or
+the Internet. The search experience should be seamless and consistent across the entire
+system, which is why Android provides a search framework to help you provide users with
a familiar search dialog and a great search experience.</p>
-<img src="{@docRoot}images/search/search-suggest-custom.png" alt="" height="417"
-style="float:right;clear:right;" />
+<div class="figure" style="width:250px">
+<img src="{@docRoot}images/search/search-suggest-custom.png" alt="" height="417" />
+<p class="img-caption"><strong>Figure 1.</strong> Screenshot of a search dialog with custom
+search suggestions.</p>
+</div>
+
+<p>Android's search framework provides a user interface in which users can perform a search and
+an interaction layer that communicates with your application, so you don't have to build
+your own search Activity. Instead, a search dialog appears at the top of the screen at the user's
+command without interrupting the current Activity.</p>
-<p>Android's search framework provides a user interface in which the user can perform a search and
-an interaction layer that communicates with your application. This way, you don't have to build
-a search box that the user must find in order to begin a search. Instead,
-a custom search dialog will appear at the top of the screen at the user's command.
-The search framework will manage the search dialog and when the user executes their search, the
-search framework will pass the query text to your application so that your application can begin a
-search. The screenshot to the right shows an example of the search dialog (using
-search suggestions).</p>
+<p>The search framework manages the life of the search dialog. When users execute a search, the
+search framework passes the query text to your application so your application can perform a
+search. Figure 1 shows an example of the search dialog with optional search suggestions.</p>
<p>Once your application is set up to use the search dialog, you can:</p>
<ul>
-<li>Customize some of the search dialog characteristics</li>
<li>Enable voice search</li>
<li>Provide search suggestions based on recent user queries</li>
-<li>Provide search suggestions that match actual results in your application data</li>
+<li>Provide custom search suggestions that match actual results in your application data</li>
<li>Offer your application's search suggestions in the system-wide Quick Search Box</li>
</ul>
-<p>The following documents will teach you how to use the search dialog in
-your application:</p>
+<p class="note"><strong>Note</strong>: The search framework does <em>not</em> provide APIs to
+search your data. To perform a search, you need to use APIs appropriate for your data. For example,
+if your data is stored in an SQLite database, you should use the {@link android.database.sqlite}
+APIs to perform searches.</p>
+
+<p>The following documents show you how to use the search dialog in your application:</p>
<dl>
<dt><strong><a href="search-dialog.html">Using the Android Search Dialog</a></strong></dt>
- <dd>How to set up your application to use the search dialog for searches. </dd>
+ <dd>How to set up your application to use the search dialog. </dd>
<dt><strong><a href="adding-recent-query-suggestions.html">Adding Recent Query
Suggestions</a></strong></dt>
<dd>How to show suggestions based on queries previously used in the search dialog.</dd>
<dt><strong><a href="adding-custom-suggestions.html">Adding Custom Suggestions</a></strong></dt>
<dd>How to show suggestions based on custom data from your application and offer your suggestions
in the system-wide Quick Search Box.</dd>
+ <dt><strong><a href="searchable-config.html">Searchable Configuration</a></strong></dt>
+ <dd>A reference for the searchable configuration file (though the other
+documents also discuss the configuration file in terms of specific behaviors).</dd>
</dl>
-<p>Also, the <strong><a href="searchable-config.html">Searchable Configuration</a></strong> document
-provides a reference for the searchable configuration file (though the above
-documents also discuss the configuration file in terms of specific behaviors).</p>
-
-<p class="note"><strong>Note</strong>: The search framework does <em>not</em> provide APIs to
-perform searches on your data. Performing actual searches is a task that you must accomplish
-using APIs appropriate for your data, such as those in {@link android.database.sqlite}
-if your data is in an SQLite database.</p>
-
<h2>Protecting User Privacy</h2>
-<p>When you implement search in your application, you should take steps to protect the user's
-privacy whenever possible. Many users consider their activities on the phone, including searches, to
-be private information. To protect the user's privacy, you should abide by the following
+<p>When you implement search in your application, take steps to protect the user's
+privacy. Many users consider their activities on the phone&mdash;including searches&mdash;to
+be private information. To protect each user's privacy, you should abide by the following
principles:</p>
<ul>
-<li><strong>Don't send personal information to servers, and if you do, don't log it.</strong>
-<p>"Personal information" is information that can personally identify your users, such as their
-name, email address, billing information, or other data which can be reasonably linked to such
-information. If
-your application implements search with the assistance of a server, try to avoid sending personal
-information along with the search queries. For example, if you are searching for businesses near a
-zip code,
-you don't need to send the user ID as well &mdash; send only the zip code to the server. If you must
-send the personal information, you should take steps to avoid logging it. If you must log it, you
-should protect that data very carefully and erase it as soon as possible.</p>
+<li><strong>Don't send personal information to servers, but if you must, do not log it.</strong>
+<p>Personal information is any information that can personally identify your users, such as their
+names, email addresses, billing information, or other data that can be reasonably linked to such
+information. If your application implements search with the assistance of a server, avoid sending
+personal information along with the search queries. For example, if you are searching for businesses
+near a zip code,
+you don't need to send the user ID as well; send only the zip code to the server. If you must
+send the personal information, you should not log it. If you must log it, protect that data
+very carefully and erase it as soon as possible.</p>
</li>
<li><strong>Provide the user with a way to clear their search history.</strong>
-<p>The search framework helps your application provide context-specific suggestions while they type.
-Sometimes these
-suggestions are based on previous searches, or other actions taken by the user in an earlier
-session. A user may not wish for previous searches to be revealed to other users, for instance if
-they share their phone with a friend. If your application provides suggestions that can reveal
-previous activities, you should implement a "Clear History" menu item, preference, or button. If you
-are
-using {@link android.provider.SearchRecentSuggestions}, you can simply call its {@link
-android.provider.SearchRecentSuggestions#clearHistory()} method. If you are implementing custom
-suggestions, you'll need to provide a
-similar "clear history" method in your provider that can be invoked by the user.</p>
+<p>The search framework helps your application provide context-specific suggestions while the user
+types. Sometimes these
+suggestions are based on previous searches or other actions taken by the user in an earlier
+session. A user might not wish for previous searches to be revealed to other device users, for
+instance, if they share their phone with a friend. If your application provides suggestions that can
+reveal previous activities, you should implement the ability for the user to clear the search
+history. If you are using {@link android.provider.SearchRecentSuggestions}, you can simply call the
+{@link android.provider.SearchRecentSuggestions#clearHistory()} method. If you are implementing
+custom suggestions, you'll need to provide a similar "clear history" method in your provider that
+the user can execute.</p>
</li>
</ul>
diff --git a/docs/html/guide/topics/search/search-dialog.jd b/docs/html/guide/topics/search/search-dialog.jd
index a5f99c7..49c6627 100644
--- a/docs/html/guide/topics/search/search-dialog.jd
+++ b/docs/html/guide/topics/search/search-dialog.jd
@@ -5,10 +5,6 @@ parent.link=index.html
<div id="qv-wrapper">
<div id="qv">
-<h2>Key classes</h2>
-<ol>
-<li>{@link android.app.SearchManager}</li>
-</ol>
<h2>In this document</h2>
<ol>
<li><a href="#TheBasics">The Basics</a></li>
@@ -27,64 +23,81 @@ parent.link=index.html
<li><a href="#SearchContextData">Passing Search Context Data</a></li>
<li><a href="#VoiceSearch">Adding Voice Search</a></li>
</ol>
+
+<h2>Key classes</h2>
+<ol>
+<li>{@link android.app.SearchManager}</li>
+</ol>
+
+<h2>Related Samples</h2>
+<ol>
+<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
+Dictionary</a></li>
+</ol>
+
+<h2>Downloads</h2>
+<ol>
+<li><a href="{@docRoot}shareables/search_icons.zip">search_icons.zip</a></li>
+</ol>
+
<h2>See also</h2>
<ol>
<li><a href="adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a></li>
<li><a href="adding-custom-suggestions.html">Adding Custom Suggestions</a></li>
<li><a href="searchable-config.html">Searchable Configuration</a></li>
-<li><a href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable
-Dictionary App</a></li>
</ol>
+
</div>
</div>
-<p>When you want to provide search in your application, the last thing you should have to worry
-about is where to put your search box. By using the Android search framework, your application will
-reveal a custom search dialog whenever the user requests it. At the
-press of a dedicated search key or an API call from your application, the search dialog will
-appear at the top of the screen and will automatically show your application icon. An example is
-shown in the screenshot below.</p>
+<p>When you want to implement search in your application, the last thing you should have to worry
+about is where to put the search box. When you implement search with the Android search framework,
+you don't have to. When the user invokes search, a search dialog appears at the top of the screen
+with your application icon to the left of the search box. When the user executes the search, your
+application receives the query so it can search your application's data. An example of the search
+dialog is shown in figure 1.</p>
-<p>This guide will teach you how to set up your application to provide search in a custom search
-dialog. In doing so, you will provide a standardized search experience and be able to add
-features like voice search and search suggestions.</p>
+<p>This guide shows you how to set up your application to provide search in the search
+dialog. When you use the search dialog, you provide a standardized search
+experience and can add features such as voice search and search suggestions.</p>
<h2 id="TheBasics">The Basics</h2>
-<img src="{@docRoot}images/search/search-ui.png" alt="" height="417"
-style="float:right;clear:right;" />
+<div class="figure" style="width:250px">
+<img src="{@docRoot}images/search/search-ui.png" alt="" height="417" />
+<p class="img-caption"><strong>Figure 1.</strong> Screenshot of an application's search dialog.</p>
+</div>
-<p>The Android search framework will manage the search dialog on your behalf; you never need
-to draw it or worry about where it is, and your current Activity will not be
-interrupted. The {@link android.app.SearchManager} is the component that does this work for
-you (hereafter, referred to as "the Search Manager"). It manages the life of the Android search
-dialog and will send your application the search query when executed by the user.</p>
+<p>The Android search framework manages the search dialog for your application. You never need
+to draw it or worry about where it is, and your Activity is not interrupted when the search dialog
+appears. The Search Manager ({@link android.app.SearchManager}) is the component that does this work
+for you. It manages the life of the search dialog and sends your application the user's search
+query.</p>
-<p>When the user executes a search, the Search Manager will use a specially-formed Intent to pass
-the search query to the Activity that you've declared to handle searches. Essentially, all you
-need is an Activity that receives this Intent, performs the search, and presents the results.
-Specifically, what you need is the following:</p>
+<p>When the user executes a search, the Search Manager creates an {@link android.content.Intent} to
+pass the search query to the Activity that you've declared to handle searches. Basically, all you
+need is an Activity that receives the search Intent, performs the search, and presents the results.
+Specifically, you need the following:</p>
<dl>
<dt>A searchable configuration</dt>
- <dd>This is an XML file that configures the search dialog and includes settings for
-features such as the hint text shown in text box and settings voice search and search
-suggestion.</dd>
+ <dd>An XML file that configures the search dialog and includes settings for features such as voice
+search, search suggestion, and the hint text.</dd>
<dt>A searchable Activity</dt>
- <dd>This is the {@link android.app.Activity} that receives the search query then
-searches your data and displays the search results.</dd>
+ <dd>The {@link android.app.Activity} that receives the search query, then searches your data and
+displays the search results.</dd>
<dt>A mechanism by which the user can invoke search</dt>
- <dd>By default, the device search key (if available) will invoke the search dialog once
-you've configured a searchable Activity. However, you should always provide another means by
-which the user can invoke a search, such as with a search button in the Options Menu or elsewhere in
-the Activity UI, because not all devices provide a dedicated search key.</dd>
+ <dd>The device search key invokes the search dialog, by default. However, a dedicated search key
+is not guaranteed on all devices, so provide another means by which the user can invoke a search,
+such as a search button in the Options Menu or elsewhere in the Activity UI.</dd>
</dl>
+
<h2 id="SearchableConfiguration">Creating a Searchable Configuration</h2>
-<p>The searchable configuration is an XML file that defines several settings for the Android search
+<p>The searchable configuration is an XML file that defines several settings for the search
dialog in your application. This file is traditionally named {@code searchable.xml} and must be
saved in the {@code res/xml/} project directory.</p>
@@ -94,45 +107,61 @@ or more attributes that configure your search dialog. For example:</p>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
- android:label="@string/app_label" >
+ android:label="@string/app_label"
+ android:hint="@string/search_hint" >
&lt;/searchable>
</pre>
-<p>This is the minimum configuration required in order to provide the search dialog. The {@code
-android:label} attribute is the only required attribute and points to a string resource, which
-should normally be the same as the application. (Although it's required, this
-label isn't actually shown to the user until you enable suggestions for Quick Search Box.)</p>
+<p>The {@code android:label} attribute is the only required attribute and points to a string
+resource, which should be the same as the application name. This label isn't actually visible to the
+user until you enable suggestions for Quick Search Box, at which point, this label is visible in the
+list of Searchable items in the system Settings.</p>
-<p>There are several other attributes accepted by the {@code &lt;searchable&gt;} element. Most of
-which apply only when configuring features such as search suggestions and voice
-search. However, we recommend that you always include the {@code android:hint} attribute, which
-specifies a string resource for the text to display in the search dialog's text box before the user
-enters their query&mdash;it provides important clues to the user about what they can search. </p>
+<p>Though it's not required, we recommend that you always include the {@code android:hint}
+attribute, which provides a hint string in the search dialog's text box before the user
+enters their query. The hint is important because it provides important clues to users about what
+they can search.</p>
<p class="note"><strong>Tip:</strong> For consistency among other
Android applications, you should format the string for {@code android:hint} as "Search
<em>&lt;content-or-product&gt;</em>". For example, "Search songs and artists" or "Search
YouTube".</p>
-<p>Next, you'll hook this configuration into your application.</p>
+<p>The {@code &lt;searchable&gt;} element accepts several other attributes. Most attributes apply
+only when configuring features such as search suggestions and voice search.</p>
+
+<p>For more details about the searchable configuration file, see the <a
+href="{@docRoot}guide/topics/search/searchable-config.html">Searchable Configuration</a>
+reference.</p>
+
<h2 id="SearchableActivity">Creating a Searchable Activity</h2>
-<p>When the user executes a search from the search dialog, the Search Manager will send
-your searchable {@link android.app.Activity} the search query with the {@link
-android.content.Intent#ACTION_SEARCH} {@link android.content.Intent}. Your searchable Activity will
-then search your data and present the results.</p>
+<p>When the user executes a search from the search dialog, the Search Manager takes the query
+and sends it to your searchable {@link android.app.Activity} in the {@link
+android.content.Intent#ACTION_SEARCH} {@link android.content.Intent}. Your searchable Activity
+then searches your data using the query and presents the results to the user.</p>
+
+<p>In order for the Search Manager to know where to deliver the search query, you must declare your
+searchable Activity in the Android manifest file.</p>
<h3 id="DeclaringSearchableActivity">Declaring a searchable Activity</h3>
-<p>If you don't have one already, create an {@link android.app.Activity} that will be used to
-perform searches, then declare it to
-accept the {@link android.content.Intent#ACTION_SEARCH} {@link android.content.Intent} and apply the
-searchable configuration. To do so, you need to add an {@code
-&lt;intent-filter&gt;} element and a {@code &lt;meta-data&gt;} element to the
-appropriate {@code &lt;activity&gt;} element in your manifest file. For example:</p>
+<p>If you don't have one already, create an {@link android.app.Activity} that performs
+searches and present search results. To set up this Activity as your searchable Activity:</p>
+<ol>
+ <li>Declare the Activity to accept the {@link android.content.Intent#ACTION_SEARCH} {@link
+android.content.Intent}, in an <a
+href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code &lt;intent-filter&gt;}</a>
+element.</li>
+ <li>Apply the searchable configuration, in a <a
+href="{@docRoot}guide/topics/manifest/meta-data-element.html">{@code &lt;meta-data&gt;}</a>
+element.</li>
+</ol>
+
+<p>For example:</p>
<pre>
&lt;application ... >
@@ -147,29 +176,27 @@ appropriate {@code &lt;activity&gt;} element in your manifest file. For example:
&lt;/application>
</pre>
-<p>The {@code android:name} attribute in the {@code &lt;meta-data&gt;} element must be defined with
-{@code "android.app.searchable"} and the {@code android:resource} attribute value must be a
-reference to the searchable configuration file saved in {@code res/xml} (in this example, it
+<p>The {@code &lt;meta-data&gt;} element must include the {@code android:name} attribute with a
+value of {@code "android.app.searchable"} and the {@code android:resource} attribute with a
+reference to the searchable configuration file (in this example, it
refers to the {@code res/xml/searchable.xml} file).</p>
-<p class="note">If you're wondering why the {@code
-&lt;intent-filter&gt;} does not include a {@code &lt;category&gt;} with the {@code DEFAULT}
-value, it's because the Intent that is delivered to this Activity when a search is executed will
-explicitly define this Activity as the component for the Intent (which the Search Manager knows
-from the searcahble meta-data declared for the Activity).</p>
+<p class="note"><strong>Note:</strong> The {@code &lt;intent-filter&gt;} does not need a <a
+href="{@docRoot}guide/topics/manifest/category-element.html">{@code &lt;category&gt;}</a> with the
+{@code DEFAULT} value, because the Search Manager delivers the {@link
+android.content.Intent#ACTION_SEARCH} Intent explicitly to your searchable Activity by name.</p>
-<p>Be aware that the search dialog will not be available from within every
-Activity of your application, by default. Rather, the search dialog will be presented to
-users only when they
+<p>The search dialog is not, by default, available from every Activity of your
+application. Rather, the search dialog is presented to users only when they
invoke search from a searchable context of your application. A searchable context is any Activity
for which you have
declared searchable meta-data in the manifest file. For example, the searchable Activity itself
(declared in the manifest snippet above) is
-a searchable context because it contains searchable meta-data that defines the
+a searchable context because it includes meta-data that defines the
searchable configuration. Any other Activity in your application is not a searchable context, by
-default, and thus, will not reveal the search dialog. You probably do want the
-search dialog to be available from every Activity in your application, so this can be easily
-fixed.</p>
+default, and thus, does not reveal the search dialog. However, you probably do want the search
+dialog available from your other activities (and to launch the searchable Activity when the user
+executes a search). You can do exactly that.</p>
<p>If you want all of your activities to provide the search dialog, add another {@code
&lt;meta-data&gt;} element inside the {@code
@@ -187,9 +214,9 @@ default searchable Activity. For example:</p>
&lt;/activity>
&lt;activity android:name=".AnotherActivity" ... >
&lt;/activity>
- &lt;!-- this one declares the searchable Activity for the whole app --&gt;
- &lt;meta-data android:name="android.app.default_searchable"
- android:value=".MySearchableActivity" />
+ &lt;!-- declare the default searchable Activity for the whole app --&gt;
+ <b>&lt;meta-data android:name="android.app.default_searchable"
+ android:value=".MySearchableActivity" /&gt;</b>
...
&lt;/application>
</pre>
@@ -199,28 +226,34 @@ default searchable Activity. For example:</p>
which it is placed (which, in this case, is the entire application). The searchable Activity to
use is specified with the {@code android:value} attribute. All other activities in the
application, such as {@code AnotherActivity}, are now considered a searchable context and can invoke
-the search dialog. When a search is executed, {@code MySearchableActivity} will
-be launched to handle the search query.</p>
+the search dialog. When a search is executed, {@code MySearchableActivity} is launched to handle
+the search query.</p>
-<p>Notice that this allows you to control which activities provide search at a more granular level.
-To specify only an individual Activity as a searchable context, simply place the {@code
+<p>You can also control which activities provide search at a more granular level.
+To specify only an individual Activity as a searchable context, place the {@code
&lt;meta-data&gt;} with the {@code
"android.app.default_searchable"} name inside the respective {@code &lt;activity&gt;}
-element (rather than inside the {@code &lt;application&gt;}). And, while it is uncommon, you can
-even create more than one searchable Activity and provide each one in different contexts of your
+element (rather than inside the {@code &lt;application&gt;} element). While uncommon, you
+can also create more than one searchable Activity and provide each one in different contexts of your
application, either by declaring a different searchable Activity in each {@code &lt;activity&gt;}
-element, or declaring a default searchable Activity for the entire application and then overriding
-it with a different {@code &lt;meta-data&gt;} element inside certain activities.</p>
+element, or by declaring a default searchable Activity for the entire application and then
+overriding it with a {@code &lt;meta-data&gt;} element inside certain activities. (You might do
+this if you want to search different sets of data that cannot be handled by the same
+searchable Activity, depending on the currently open Activity.)</p>
<h3 id="PerformingSearch">Performing a search</h3>
-<p>Once your Activity is declared searchable, performing the actual search involves three steps:
-receiving the query, searching your data, and presenting the results.</p>
+<p>Once you have declared your searchable Activity, performing a search for the user involves
+three steps:</p>
+<ol>
+ <li><a href="#ReceivingTheQuery">Receiving the query</a></li>
+ <li><a href="#SearchingYourData">Searching your data</a></li>
+ <li><a href="#PresentingTheResults">Presenting the results</a></li>
+</ol>
-<p>Traditionally, your search results should be presented in a {@link android.widget.ListView}
-(assuming that our results are text-based), so
-you may want your searchable Activity to extend {@link android.app.ListActivity}, which
+<p>Traditionally, your search results should be presented in a {@link android.widget.ListView}, so
+you might want your searchable Activity to extend {@link android.app.ListActivity}, which
provides easy access to {@link android.widget.ListView} APIs. (See the <a
href="{@docRoot}resources/tutorials/views/hello-listview.html">List View Tutorial</a> for a simple
{@link android.app.ListActivity} sample.)</p>
@@ -228,12 +261,12 @@ href="{@docRoot}resources/tutorials/views/hello-listview.html">List View Tutoria
<h4 id="ReceivingTheQuery">Receiving the query</h4>
-<p>When a search is executed from the search dialog, your searchable Activity will be opened
-with the {@link android.content.Intent#ACTION_SEARCH} {@link android.content.Intent}, which carries
-the search query in the
-{@link android.app.SearchManager#QUERY QUERY} extra. All you need to do is check for
-this Intent and extract the string. For example, here's how you can get the query when your
-Activity launches:</p>
+<p>When a user executes a search from the search dialog, the Search Manager sends the {@link
+android.content.Intent#ACTION_SEARCH} {@link android.content.Intent} to your searchable Activity.
+This Intent carries the search query in the
+{@link android.app.SearchManager#QUERY QUERY} string extra. You must check for
+this Intent when the Activity starts and extract the string. For example, here's how you can get the
+query when your Activity starts:</p>
<pre>
&#64;Override
@@ -258,22 +291,21 @@ is done.</p>
<h4 id="SearchingYourData">Searching your data</h4>
-<p>The process of storing and searching your data is a process that's unique to your application.
-There are many ways that you might do this and discussing all options is beyond the scope of
-this document. This guide will not teach you how to store your data and search it; this
-is something you must carefully consider in terms of your needs and your data. However, here are
-some tips you may be able to apply:</p>
+<p>The process of storing and searching your data is unique to your application.
+You can store and search your data in many ways, but this guide does not show you how to store your
+data and search it. Storing and searching your data is something you should carefully consider in
+terms of your needs and your data. However, here are some tips you might be able to apply:</p>
<ul>
<li>If your data is stored in a SQLite database on the device, performing a full-text search
(using FTS3, rather than a LIKE query) can provide a more robust search across text data and can
-produce results many, many times faster. See <a href="http://sqlite.org/fts3.html">sqlite.org</a>
+produce results significantly faster. See <a href="http://sqlite.org/fts3.html">sqlite.org</a>
for information about FTS3 and the {@link android.database.sqlite.SQLiteDatabase} class for
information about SQLite on Android. Also look at the <a
href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary</a> sample
application to see a complete SQLite implementation that performs searches with FTS3.</li>
- <li>If your data is stored online, then the perceived search performance may be
-inhibited by the user's data connection. You may want to display a spinning progress wheel until
+ <li>If your data is stored online, then the perceived search performance might be
+inhibited by the user's data connection. You might want to display a spinning progress wheel until
your search returns. See {@link android.net} for a reference of network APIs and <a
href="{@docRoot}guide/topics/ui/dialogs.html#ProgressDialog">Creating a Progress Dialog</a> to see
how you can display a progress wheel.</li>
@@ -283,32 +315,32 @@ how you can display a progress wheel.</li>
<div class="sidebox-wrapper">
<div class="sidebox">
<h2>About Adapters</h2>
-<p>An Adapter will bind individual items from a set of data into individual {@link
+<p>An Adapter binds individual items from a set of data into individual {@link
android.view.View} objects. When the Adapter
is applied to a {@link android.widget.ListView}, the Views are injected as individual items of the
list. {@link
android.widget.Adapter} is simply an interface, so implementations such as {@link
android.widget.CursorAdapter} (for binding data from a {@link android.database.Cursor}) are needed.
If none of the existing implementations work for your data, then you should implement your own from
-{@link android.widget.BaseAdapter}. Install the SDK Samples package for API Level 4 to see a
-version of the Searchable Dictionary that creates a custom BaseAdapter.</p>
+{@link android.widget.BaseAdapter}. Install the SDK Samples package for API Level 4 to see the
+original version of the Searchable Dictionary, which creates a custom BaseAdapter.</p>
</div>
</div>
<p>Regardless of where your data lives and how you search it, we recommend that you return search
results to your searchable Activity with an {@link android.widget.Adapter}. This way, you can easily
present all the search results in a {@link android.widget.ListView}. If your data comes from a
-SQLite database query, then you can easily apply your results to a {@link android.widget.ListView}
+SQLite database query, then you can apply your results to a {@link android.widget.ListView}
using a {@link android.widget.CursorAdapter}. If your data comes in some other type of format, then
you can create an extension of the {@link android.widget.BaseAdapter}.</p>
<h4 id="PresentingTheResults">Presenting the results</h4>
-<p>Presenting your search results is mostly a UI detail and not something covered by the search
-framework APIs. However, a simple solution is to create your searchable Activity to extend {@link
-android.app.ListActivity} and then call {@link
+<p>Presenting your search results is mostly a UI detail that is not handled by the search APIs.
+However, one option is to create your searchable Activity to extend {@link
+android.app.ListActivity} and call {@link
android.app.ListActivity#setListAdapter(ListAdapter)}, passing it an {@link
-android.widget.Adapter} that is bound to your data. This will automatically project all the
+android.widget.Adapter} that is bound to your data. This injects all the
results into the Activity {@link android.widget.ListView}.</p>
<p>For more help presenting your results, see the {@link android.app.ListActivity}
@@ -316,29 +348,27 @@ documentation.</p>
<p>Also see the <a
href="{@docRoot}resources/samples/SearchableDictionary/index.html">Searchable Dictionary</a> sample
-application for an a complete demonstration of how to search an SQLite database and use an
-{@link android.widget.Adapter} to provide resuls in a {@link android.widget.ListView}.</p>
+for an a complete demonstration of how to search an SQLite database and use an
+{@link android.widget.Adapter} to provide results in a {@link android.widget.ListView}.</p>
+
<h2 id="InvokingTheSearchDialog">Invoking the Search Dialog</h2>
-<p>Once you have a searchable Activity in place, invoking the search dialog so the user can
-submit a
-query is easy. Many Android devices provide a dedicated search key and when it is pressed while the
-user is within a searchable context of your application, the search dialog will be revealed.
-However,
-you should never assume that a search key is available on the user's device and should always
-provide a search button in your UI that will invoke search.</p>
+<p>Once you have a searchable Activity, invoking the search dialog is easy. Many Android
+devices provide a dedicated SEARCH key, which reveals the search dialog when the user presses it
+from a searchable context of your application. However, you should not assume that a SEARCH
+key is available on the user's device and should always provide a search button in your UI that
+invokes search.</p>
-<p>To invoke search from your Activity, simply call {@link
-android.app.Activity#onSearchRequested()}.</p>
+<p>To invoke search from your Activity, call {@link android.app.Activity#onSearchRequested()}.</p>
-<p>For example, you should provide a menu item in your <a
+<p>For instance, you should provide a menu item in your <a
href="{@docRoot}guide/topics/ui/menus.html#options-menu">Options Menu</a> or a button in your UI to
-invoke search with this method. For your convenience, this <a
+invoke search with this method. The <a
href="{@docRoot}shareables/search_icons.zip">search_icons.zip</a> file includes icons for
-medium and high density screens, which you can use for your menu item or button (low density
-screens will automatically scale-down the hdpi image by one half). </p>
+medium and high density screens, which you can use for your search menu item or button (low density
+screens automatically scale-down the hdpi image by one half). </p>
<!-- ... maybe this should go into the Creating Menus document ....
<p>If you chose to provide a shortcut key for the menu item, using {@link
@@ -346,28 +376,29 @@ android.view.MenuItem#setAlphabeticShortcut(char)}, then SearchManager.MENU_KEY
key character, representing the default search key.</p>
-->
-<p>You can also enable "type-to-search" functionality in your Activity by calling {@link
-android.app.Activity#setDefaultKeyMode(int) setDefaultKeyMode}({@link
-android.app.Activity#DEFAULT_KEYS_SEARCH_LOCAL}). When this is enabled and the user begins typing on
-the keyboard, search will automatically be
-invoked and the keystrokes will be inserted in the search dialog. Be sure to enable this mode
-during your Activity {@link android.app.Activity#onCreate(Bundle) onCreate()} method.</p>
+<p>You can also enable "type-to-search" functionality, which reveals the search dialog when the
+user starts typing on the keyboard and the keystrokes are inserted into the search dialog. You can
+enable type-to-search in your Activity by calling
+{@link android.app.Activity#setDefaultKeyMode(int) setDefaultKeyMode}({@link
+android.app.Activity#DEFAULT_KEYS_SEARCH_LOCAL}) during your Activity's
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method.</p>
-<h3 id="LifeCycle">The impact of the search dialog on your Activity life-cycle</h3>
+<h3 id="LifeCycle">The impact of the search dialog on your Activity lifecycle</h3>
-<p>The search dialog behaves like a {@link android.app.Dialog} that floats at the top of the
-screen. It
-does not cause any change in the Activity stack, so no life-cycle methods (such as {@link
-android.app.Activity#onPause()}) will
-be called. All that happens is your Activity loses input focus as it is given to the search dialog.
+<p>The search dialog is a {@link android.app.Dialog} that floats at the top of the
+screen. It does not cause any change in the Activity stack, so when the search dialog appears, no
+lifecycle methods for the currently open Activity (such as {@link
+android.app.Activity#onPause()}) are called. Your Activity just loses input focus as it is given to
+the search dialog.
</p>
-<p>If you want to be notified when search is invoked, simply override the {@link
-android.app.Activity#onSearchRequested()} method. When this is called, you can do any work you may
-want to do when your Activity looses input focus (such as pause animations). But unless you are
-<a href="#SearchContextData">Passing Search Context Data</a> (discussed above), you should always
-call the super class implementation. For example:</p>
+<p>If you want to be notified when search is invoked, override the {@link
+android.app.Activity#onSearchRequested()} method. When the system calls this method, you can do any
+work you want to when your Activity looses input focus to the search dialog (such as pause
+animations). Unless you are <a href="#SearchContextData">passing search context data</a>
+(discussed below), you should end the method by calling the super class implementation. For
+example:</p>
<pre>
&#64;Override
@@ -377,35 +408,37 @@ public boolean onSearchRequested() {
}
</pre>
-<p>If the user cancels search by pressing the device Back key, the Activity in which search was
-invoked will re-gain input focus. You can register to be notified when the search dialog is
-closed with {@link android.app.SearchManager#setOnDismissListener(SearchManager.OnDismissListener)}
-and/or {@link android.app.SearchManager#setOnCancelListener(SearchManager.OnCancelListener)}. You
-should normally only need to register the {@link android.app.SearchManager.OnDismissListener
-OnDismissListener}, because this is called every time that the search dialog is closed. The {@link
+<p>If the user cancels search by pressing the BACK key, the Activity in which search was
+invoked re-gains input focus. You can register to be notified when the search dialog is
+closed with {@link android.app.SearchManager#setOnDismissListener(SearchManager.OnDismissListener)
+setOnDismissListener()}
+and/or {@link android.app.SearchManager#setOnCancelListener(SearchManager.OnCancelListener)
+setOnCancelListener()}. You
+should need to register only the {@link android.app.SearchManager.OnDismissListener
+OnDismissListener}, because it is called every time the search dialog closes. The {@link
android.app.SearchManager.OnCancelListener OnCancelListener} only pertains to events in which the
-user explicitly left the search dialog, so it is not called when a search is executed (in which
+user explicitly exited the search dialog, so it is not called when a search is executed (in which
case, the search dialog naturally disappears).</p>
-<p>If the current Activity is not the searchable Activity, then the normal Activity life-cycle
-events will be triggered once the user executes a search (the current Activity will receive {@link
+<p>If the current Activity is not the searchable Activity, then the normal Activity lifecycle
+events are triggered once the user executes a search (the current Activity receives {@link
android.app.Activity#onPause()} and so forth, as
described in <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Application
Fundamentals</a>). If, however, the current Activity is the searchable Activity, then one of two
-things will happen:</p>
+things happens:</p>
-<ul>
- <li>By default, the searchable Activity will receive the {@link
+<ol type="a">
+ <li>By default, the searchable Activity receives the {@link
android.content.Intent#ACTION_SEARCH} Intent with a call to {@link
android.app.Activity#onCreate(Bundle) onCreate()} and a new instance of the
-Activity will be brought to the top of the stack. You'll now have two instances of your searchable
-Activity in the Activity stack (so pressing the Back key will go back to the previous instance of
-the searchable Activity, rather than exiting the searchable Activity).</li>
- <li>On the other hand, if the Activity has set {@code android:launchMode} to "singleTop" then the
-searchable Activity will receive the {@link android.content.Intent#ACTION_SEARCH} Intent with a call
+Activity is brought to the top of the Activity stack. There are now two instances of your
+searchable Activity in the Activity stack (so pressing the BACK key goes back to the previous
+instance of the searchable Activity, rather than exiting the searchable Activity).</li>
+ <li>If you set {@code android:launchMode} to "singleTop", then the
+searchable Activity receives the {@link android.content.Intent#ACTION_SEARCH} Intent with a call
to {@link android.app.Activity#onNewIntent(Intent)}, passing the new {@link
-android.content.Intent#ACTION_SEARCH} Intent here. For example, here's how you might want to handle
-this case:
+android.content.Intent#ACTION_SEARCH} Intent here. For example, here's how you might handle
+this case, in which the searchable Activity's launch mode is "singleTop":
<pre>
&#64;Override
public void onCreate(Bundle savedInstanceState) {
@@ -430,28 +463,30 @@ private void handleIntent(Intent intent) {
<p>Compared to the example code in the section about <a href="#PerfomingSearch">Performing a
Search</a>, all the code to handle the
-search Intent has been moved outside the {@link android.app.Activity#onCreate(Bundle)
-onCreate()} method so it can also be executed from {@link android.app.Activity#onNewIntent(Intent)
-onNewIntent()}.
-It's important to note that when {@link android.app.Activity#onNewIntent(Intent)} is
-called, the Activity has not been restarted, so the {@link android.app.Activity#getIntent()} method
-will still return the Intent that was first received with {@link
-android.app.Activity#onCreate(Bundle) onCreate()}. This is why {@link
-android.app.Activity#setIntent(Intent)} is called inside {@link
-android.app.Activity#onNewIntent(Intent)} (just in case you call {@link
-android.app.Activity#getIntent()} at a later time).</p>
+search Intent is now in the {@code handleIntent()} method, so that both {@link
+android.app.Activity#onCreate(Bundle)
+onCreate()} and {@link android.app.Activity#onNewIntent(Intent) onNewIntent()} can execute it.</p>
+
+<p>When the system calls {@link android.app.Activity#onNewIntent(Intent)}, the Activity has
+not been restarted, so the {@link android.app.Activity#getIntent()} method
+returns the same Intent that was received with {@link
+android.app.Activity#onCreate(Bundle) onCreate()}. This is why you should call {@link
+android.app.Activity#setIntent(Intent)} inside {@link
+android.app.Activity#onNewIntent(Intent)} (so that the Intent saved by the Activity is updated in
+case you call {@link android.app.Activity#getIntent()} in the future).</p>
</li>
-</ul>
+</ol>
-<p>This second scenario is normally ideal, because the chances are good that once a search is
-completed, the user will perform additional searches and it's a bad experience if your application
-piles multiple instances of the searchable Activity on the stack. So we recommend that you set your
-searchable Activity to "singleTop" launch mode in the application manifest. For example:</p>
+<p>The second scenario using "singleTop" launch mode is usually ideal, because chances are good that
+once a search is done, the user will perform additional searches and it's a bad experience if your
+application creates multiple instances of the searchable Activity. So, we recommend that you set
+your searchable Activity to "singleTop" launch mode in the application
+manifest. For example:</p>
<pre>
&lt;activity android:name=".MySearchableActivity"
- android:launchMode="singleTop" >
+ <b>android:launchMode="singleTop"</b> >
&lt;intent-filter>
&lt;action android:name="android.intent.action.SEARCH" />
&lt;/intent-filter>
@@ -461,19 +496,20 @@ searchable Activity to "singleTop" launch mode in the application manifest. For
</pre>
+
<h2 id="SearchContextData">Passing Search Context Data</h2>
-<p>In order to refine your search criteria, you may want to provide some additional
-data to your searchable Activity when a search is executed. For instance, when you search your data,
-you may want to filter results based on more than just the search query text. In a simple
-case, you could just make your refinements inside the searchable Activity, for every search made.
-If, however, your
-search criteria may vary from one searchable context to another, then you can pass whatever data is
-necessary to refine your search in the {@link android.app.SearchManager#APP_DATA} Bundle, which is
-included in the {@link android.content.Intent#ACTION_SEARCH} Intent.</p>
-
-<p>To pass this kind of data to your searchable Activity, you need to override {@link
-android.app.Activity#onSearchRequested()} method for the Activity in which search will be invoked.
+<p>To refine your search criteria from the current Activity instead of depending only on the user's
+search query, you can provide additional data in the Intent that the Search Manager sends to your
+searchable Activity. In a simple case, you can make your refinements inside the searchable
+Activity, for every search made, but if your
+search criteria varies from one searchable context to another, then you can pass whatever data
+is necessary to refine your search in the {@link android.app.SearchManager#APP_DATA} {@link
+android.os.Bundle}, which is included in the {@link android.content.Intent#ACTION_SEARCH}
+Intent.</p>
+
+<p>To pass this kind of data to your searchable Activity, override {@link
+android.app.Activity#onSearchRequested()} method for the Activity in which search can be invoked.
For example:</p>
<pre>
@@ -487,56 +523,54 @@ public boolean onSearchRequested() {
</pre>
<p>Returning "true" indicates that you have successfully handled this callback event. Then in your
-searchable Activity, you can extract this data from the {@link
+searchable Activity, you can extract the data placed inside {@code appdata} from the {@link
android.app.SearchManager#APP_DATA} {@link android.os.Bundle} to refine the search. For example:</p>
<pre>
- Bundle appData = getIntent().getBundleExtra(SearchManager.APP_DATA);
- if (appData != null) {
- boolean jargon = appData.getBoolean(MySearchableActivity.JARGON);
- }
+Bundle appData = getIntent().getBundleExtra(SearchManager.APP_DATA);
+if (appData != null) {
+ boolean jargon = appData.getBoolean(MySearchableActivity.JARGON);
+}
</pre>
-<p class="caution"><strong>Note:</strong> You should never call the {@link
+<p class="caution"><strong>Caution:</strong> Never call the {@link
android.app.Activity#startSearch(String,boolean,Bundle,boolean) startSearch()} method from outside
-the {@link android.app.Activity#onSearchRequested()} callback method. When you want to invoke the
-search dialog, always call {@link android.app.Activity#onSearchRequested()} so that custom
-implementations (such as the addition of {@code appData}, in the above example) can be accounted
-for.</p>
+the {@link android.app.Activity#onSearchRequested()} callback method. To invoke the search dialog
+in your Activity, always call {@link android.app.Activity#onSearchRequested()}. Otherwise, {@link
+android.app.Activity#onSearchRequested()} is not called and customizations (such as the addition of
+{@code appData} in the above example) are missed.</p>
<h2 id="VoiceSearch">Adding Voice Search</h2>
-<p>You can easily add voice search functionality to your search dialog by adding the {@code
-android:voiceSearchMode} attribute to your searchable configuration. This will add a voice search
-button in the search dialog that, when clicked, will launch a voice prompt. When the user
-has finished speaking, the transcribed search query will be sent to your searchable
+<p>You can add voice search functionality to your search dialog by adding the {@code
+android:voiceSearchMode} attribute to your searchable configuration. This adds a voice search
+button in the search dialog that launches a voice prompt. When the user
+has finished speaking, the transcribed search query is sent to your searchable
Activity.</p>
-<p>To enable voice search for your activity, add the {@code android:voiceSearchMode}
-attribute to your searchable configuration. For example:</p>
+<p>For example:</p>
<pre>
&lt;?xml version="1.0" encoding="utf-8"?>
&lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"
- android:voiceSearchMode="showVoiceSearchButton|launchRecognizer" >
+ <b>android:voiceSearchMode="showVoiceSearchButton|launchRecognizer"</b> >
&lt;/searchable>
</pre>
<p>The value {@code showVoiceSearchButton} is required to enable voice
search, while the second value, {@code launchRecognizer}, specifies that the voice search button
-should launch a recognizer that returns the transcribed text to the searchable Activity. This is
-how most applications should declare this attribute.</p>
+should launch a recognizer that returns the transcribed text to the searchable Activity.</p>
-<p>There are some additional attributes you can provide to specify the voice search behavior, such
+<p>You can provide additional attributes to specify the voice search behavior, such
as the language to be expected and the maximum number of results to return. See the <a
-href="searchable-config.html">Searchable Configuration</a> for more information about the
+href="searchable-config.html">Searchable Configuration</a> reference for more information about the
available attributes.</p>
<p class="note"><strong>Note:</strong> Carefully consider whether voice search is appropriate for
-your application. All searches performed with the voice search button will be immediately sent to
-your searchable Activity without a chance for the user to review the transcribed query. Be sure to
-sufficiently test the voice recognition and ensure that it understands the types of queries that
-the user will submit inside your application.</p>
+your application. All searches performed with the voice search button are immediately sent to
+your searchable Activity without a chance for the user to review the transcribed query. Sufficiently
+test the voice recognition and ensure that it understands the types of queries that
+the user might submit inside your application.</p>
diff --git a/docs/html/guide/topics/search/searchable-config.jd b/docs/html/guide/topics/search/searchable-config.jd
index 71566de..2aa2db6 100644
--- a/docs/html/guide/topics/search/searchable-config.jd
+++ b/docs/html/guide/topics/search/searchable-config.jd
@@ -14,18 +14,18 @@ parent.link=index.html
</div>
</div>
-<p>In order to utilize the Android search framework and provide a custom search dialog, your
+<p>To utilize the Android search framework and provide a custom search dialog, your
application must provide a search
configuration in the form of an XML resource. This document describes the search configuration XML
-in terms of its syntax and usage. For a more complete discussion about how to implement search
-features for your application, see the companion documents about <a
+in terms of its syntax and usage. For more information about how to implement search
+features for your application, see the developer guide about <a
href="index.html">Search</a>.</p>
<dl class="xml">
<dt>file location:</dt>
<dd><code>res/xml/<em>filename</em>.xml</code><br/>
-The filename will be used as the resource ID.</dd>
+Android uses the filename as the resource ID.</dd>
<dt>syntax:</dt>
<dd>
@@ -70,174 +70,187 @@ The filename will be used as the resource ID.</dd>
<p class="caps">attributes:</p>
<dl class="atn-list">
<dt><code>android:label</code></dt>
- <dd><em>String resource</em>. <strong>Required</strong>. This is the name of your application.
-It should normally be the same as the name applied to the {@code android:label} attribute of your <a
+ <dd><em>String resource</em>. (Required.) The name of your application.
+It should be the same as the name applied to the {@code android:label} attribute of your <a
href="{@docRoot}guide/topics/manifest/activity-element.html#label">{@code &lt;activity&gt;}</a> or
<a href="{@docRoot}guide/topics/manifest/application-element.html#label">{@code
-&lt;application&gt;}</a> manifest element. This is only visible to the user when you set
-<code>android:includeInGlobalSearch</code> "true", in which case, this label is used to identify
+&lt;application&gt;}</a> manifest element. This label is only visible to the user when you set
+<code>android:includeInGlobalSearch</code> to "true", in which case, this label is used to identify
your application as a searchable item in the system's search settings.</dd>
+
<dt><code>android:hint</code></dt>
- <dd><em>String resource</em>. The text to display in the search text field when no text has
- been entered. This is recommended in order to provide a hint to the user about what
-content is searchable. For consistency among other Android applications, you should format the
+ <dd><em>String resource</em>. (Recommended.) The text to display in the search text field when
+no text has been entered. It provides a hint to the user about what
+content is searchable. For consistency with other Android applications, you should format the
string for {@code android:hint} as "Search <em>&lt;content-or-product&gt;</em>". For example,
"Search songs and artists" or "Search YouTube".</dd>
+
<dt><code>android:searchMode</code></dt>
- <dd><em>Keyword</em>. Sets additional modes that control the search presentation.
-Specifically, the available modes define how the query text in the search dialog's text box
-should be rewritten when a suggestion is focused. The following mode values are accepted:
+ <dd><em>Keyword</em>. Sets additional modes that control the search dialog presentation.
+Currently available modes define how the query text that appears in the search dialog
+should be rewritten when a custom suggestion receives focus. The following mode values are accepted:
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr>
- <td><code>"queryRewriteFromData"</code></td>
- <td>If set, this causes the suggestion column
- {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} to be considered as the
-text for suggestion query
- rewriting. This should only be used when the values in
- {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} are suitable for user
-inspection and editing -
- typically, HTTP/HTTPS Uri's.</td>
+ <td><code>"queryRewriteFromText"</code></td>
+ <td>Use the value from the {@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1}
+colum to rewrite the query text in the search dialog.</td>
</tr>
<tr>
- <td><code>"queryRewriteFromText"</code></td>
- <td>If set, this causes the suggestion
- column {@link android.app.SearchManager#SUGGEST_COLUMN_TEXT_1} to be considered as the
-text for suggestion query
- rewriting. This should be used for suggestions in which no query
- text is provided and the {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA}
-values are not suitable
- for user inspection and editing.</td>
+ <td><code>"queryRewriteFromData"</code></td>
+ <td>Use the value from the
+ {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column to rewrite the
+query text in the search dialog. This should only be used when the values in
+ {@link android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} are suitable for user
+inspection and editing, typically HTTP URI's.</td>
</tr>
</table>
<p>For more information, see the discussion about rewriting the query text in <a
href="adding-custom-suggestions.html#RewritingQueryText">Adding Custom Suggestions</a>.</p>
</dd>
+
<dt><code>android:searchButtonText</code></dt>
- <dd><em>String resource</em>. The text to display in the button that executes the search. By
+ <dd><em>String resource</em>. The text to display in the button that executes search. By
default, the button shows a search icon (a magnifying glass), which is ideal for
-internationalization.</dd>
+internationalization, so you should not use this attribute to change the button unless the
+behavior is something other than a search (such as a URL request in a web browser).</dd>
+
<dt><code>android:inputType</code></dt>
- <dd><em>Keyword</em>. Defines the type of input method (soft-keyboard) to use with the search
-dialog. For most searches, in which free form text is expected, this attribute is not needed and
-the default input method should be used. See {@link android.R.attr#inputType} for a list of suitable
-values for this attribute.</dd>
+ <dd><em>Keyword</em>. Defines the type of input method (such as the type of soft keyboard)
+to use with the search dialog. For most searches, in which free-form text is expected, you don't
+need this attribute. See {@link android.R.attr#inputType} for a list of suitable values for this
+attribute.</dd>
+
<dt><code>android:imeOptions</code></dt>
<dd><em>Keyword</em>. Supplies additional options for the input method.
- For most searches, in which free form text is expected, this attribute is not needed,
- and will default to "actionSearch" (provides the "search" button instead of a carriage
-return). See {@link android.R.attr#imeOptions} for a list of suitable values for this attribute.
+ For most searches, in which free-form text is expected, you don't need this attribute. The
+default IME is "actionSearch" (provides the "search" button instead of a carriage
+return in the soft keyboard). See {@link android.R.attr#imeOptions} for a list of suitable values
+for this attribute.
</dd>
</dl>
+
+ <h4>Search suggestion attributes</h4>
+
<p>If you have defined a content provider to generate search suggestions, you need to
- define additional attributes in order to configure communications with the Content
- Provider. When providing search suggestions, you'll need some of the following
+ define additional attributes that configure communications with the content
+ provider. When providing search suggestions, you need some of the following
{@code &lt;searchable>} attributes:</p><br/>
<dl class="atn-list">
<dt><code>android:searchSuggestAuthority</code></dt>
- <dd><em>String</em>. <strong>Required to provide search suggestions</strong>.
+ <dd><em>String</em>. (Required to provide search suggestions.)
This value must match the authority string provided in the {@code android:authorities}
-attribute of the {@code &lt;provider>} element.</dd>
+attribute of the Android manifest {@code &lt;provider>} element.</dd>
+
<dt><code>android:searchSuggestPath</code></dt>
- <dd><em>String</em>. This path will be used as a portion of the suggestions
+ <dd><em>String</em>. This path is used as a portion of the suggestions
query {@link android.net.Uri}, after the prefix and authority, but before
the standard suggestions path.
This is only required if you have a single content provider issuing different types
- of suggestions (e.g. for different data types) and you need
- a way to disambiguate the suggestions queries when they are received.</dd>
+ of suggestions (such as for different data types) and you need
+ a way to disambiguate the suggestions queries when you receive them.</dd>
+
<dt><code>android:searchSuggestSelection</code></dt>
- <dd><em>String</em>. This value will be passed into your
- query function as the {@code selection} parameter. Typically this will be a WHERE clause
-for your database, and should contain a single question mark, which is a place-holder for the
-actual query string that has been typed by the user. However, you can also use any non-null
-value to simply trigger the delivery of the query text via the {@code
+ <dd><em>String</em>. This value is passed into your
+ query function as the {@code selection} parameter. Typically this is a WHERE clause
+for your database, and should contain a single question mark, which is a placeholder for the
+actual query string that has been typed by the user (for example, {@code "query=?"}). However, you
+can also use any non-null value to trigger the delivery of the query text via the {@code
selectionArgs} parameter (and then ignore the {@code selection} parameter).</dd>
+
<dt><code>android:searchSuggestIntentAction</code></dt>
<dd><em>String</em>. The default Intent action to be used when a user
- clicks on a search suggestion (such as {@code "android.intent.action.VIEW"}).
- If not overridden by the selected suggestion (via the {@link
-android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column), this value will
- be placed in the action field of the {@link android.content.Intent} when the
- user clicks a suggestion.</dd>
+ clicks on a custom search suggestion (such as {@code "android.intent.action.VIEW"}).
+ If this is not overridden by the selected suggestion (via the {@link
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_ACTION} column), this value is placed in the action
+field of the {@link android.content.Intent} when the user clicks a suggestion.</dd>
+
<dt><code>android:searchSuggestIntentData</code></dt>
<dd><em>String</em>. The default Intent data to be used when a user
- clicks on a search suggestion.
+ clicks on a custom search suggestion.
If not overridden by the selected suggestion (via the {@link
-android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column), this value will be
+android.app.SearchManager#SUGGEST_COLUMN_INTENT_DATA} column), this value is
placed in the data field of the {@link android.content.Intent} when the user clicks
a suggestion.</dd>
+
<dt><code>android:searchSuggestThreshold</code></dt>
<dd><em>Integer</em>. The minimum number of characters needed to
- trigger a suggestion look-up. Only guarantees that a source will not be
- queried for anything shorter than the threshold. The default value is 0.</dd>
+ trigger a suggestion look-up. Only guarantees that the Search Manager will not query your
+ content provider for anything shorter than the threshold. The default value is 0.</dd>
</dl>
<p>For more information about the above attributes for search suggestions, see the guides for
<a href="adding-recent-query-suggestions.html">Adding Recent Query Suggestions</a> and
<a href="adding-custom-suggestions.html">Adding Custom Suggestions</a>.</p>
- <p>Beyond providing search suggestions while using your application's search dialog, you
- can also configure your search suggestions to be made available to Quick Search Box,
- which will allow users so receive search suggestions from your application content from outside
- your application. When providing search suggestions to Quick Search Box, you'll need some of the
+
+ <h4>Quick Search Box attributes</h4>
+
+ <p>To make your custom search suggestions available to Quick Search Box, you need some of the
following {@code &lt;searchable>} attributes:</p><br/>
<dl class="atn-list">
<dt><code>android:includeInGlobalSearch</code></dt>
- <dd><em>Boolean</em>. <strong>Required to provide search suggestions in
- Quick Search Box</strong>. "true" if you want your suggestions to be
- included in the globally accessible Quick Search Box. Note that the user must
- still enable your application as a searchable item in the system search settings in order
- for your suggestions to appear in Quick Search Box.</dd>
+ <dd><em>Boolean</em>. (Required to provide search suggestions in
+ Quick Search Box.) Set to "true" if you want your suggestions to be
+ included in the globally accessible Quick Search Box. The user must
+ still enable your application as a searchable item in the system search settings before
+ your suggestions will appear in Quick Search Box.</dd>
+
<dt><code>android:searchSettingsDescription</code></dt>
<dd><em>String</em>. Provides a brief description of the search suggestions that you provide
-to Quick Search Box, which will be displayed in the searchable items entry for your application.
+to Quick Search Box, which is displayed in the searchable items entry for your application.
Your description should concisely describe the content that is searchable. For example, "Artists,
albums, and tracks" for a music application, or "Saved notes" for a notepad application.</dd>
+
<dt><code>android:queryAfterZeroResults</code></dt>
- <dd><em>Boolean</em>. "true" if you want your content provider to be invoked for
- supersets of queries that have returned zero results for in the past. For example, if a
- source returned zero results for "bo", it would be ignored for "bob". If "false",
- this source will only be ignored for a single session; the next time the search dialog
- is invoked, all sources will be queried. The default value is false.</dd>
+ <dd><em>Boolean</em>. Set to "true" if you want your content provider to be invoked for
+ supersets of queries that have returned zero results in the past. For example, if
+your content provider returned zero results for "bo", it should be requiried for "bob". If set to
+"false", supersets are ignored for a single session ("bob" does not invoke a requery). This lasts
+only for the life of the search dialog (when the search dialog is reopened, "bo" queries your
+content provider again). The default value is false.</dd>
</dl>
+
+ <h4>Voice search attributes</h4>
+
<p>To enable voice search for your search dialog, you'll need some of the
following {@code &lt;searchable>} attributes:</p><br/>
<dl class="atn-list">
<dt><code>android:voiceSearchMode</code></dt>
- <dd><em>Keyword</em>. <strong>Required to provide voice search capabilities</strong>.
+ <dd><em>Keyword</em>. (Required to provide voice search capabilities.)
Enables voice search for the search dialog, with a specific mode for voice search.
- (Voice search may not be provided by the device, in which case these flags will
+ (Voice search may not be provided by the device, in which case these flags
have no effect.) The following mode values are accepted:
<table>
<tr><th>Value</th><th>Description</th></tr>
<tr>
<td><code>"showVoiceSearchButton"</code></td>
- <td>Display a voice search button. This only
- takes effect if voice search is available on the device. If set, then either
- {@code "launchWebSearch"} or {@code "launchRecognizer"} must also be set
+ <td>Display a voice search button, if voice search is available on the device. If set,
+then either {@code "launchWebSearch"} or {@code "launchRecognizer"} must also be set
(separated by the pipe | character).</td>
</tr>
<tr>
<td><code>"launchWebSearch"</code></td>
- <td>The voice search button will take the user directly
- to a built-in voice web search activity. Most applications will not use this flag, as
- it will take the user away from the Activity in which search was invoked.</td>
+ <td>The voice search button takes the user directly
+ to a built-in voice web search activity. Most applications don't need this flag, as
+ it takes the user away from the Activity in which search was invoked.</td>
</tr>
<tr>
<td><code>"launchRecognizer"</code></td>
- <td>The voice search button will take
+ <td>The voice search button takes
the user directly to a built-in voice recording activity. This Activity
- will prompt the user to speak, transcribe the spoken text, and forward the resulting
- query text to the searchable Activity, just as if the user had typed it into the
+ prompts the user to speak, transcribes the spoken text, and forwards the resulting
+ query text to the searchable Activity, just as if the user typed it into the
search UI and clicked the search button.</td>
</tr>
</table>
</dd>
+
<dt><code>android:voiceLanguageModel</code></dt>
<dd><em>Keyword</em>. The language model that
should be used by the voice recognition system. The following values are accepted:
@@ -245,92 +258,99 @@ albums, and tracks" for a music application, or "Saved notes" for a notepad appl
<tr><th>Value</th><th>Description</th></tr>
<tr>
<td><code>"free_form"</code></td>
- <td>Use a language model based on free-form speech recognition. This is the
-default.</td>
+ <td>Use free-form speech recognition for dictating queries. This is primarily
+optimized for English. This is the default.</td>
</tr>
<tr>
<td><code>"web_search"</code></td>
- <td>Use a language model based on web search terms.</td>
+ <td>Use web-search-term recognition for shorter, search-like phrases. This is
+available in more languages than "free_form".</td>
</tr>
</table>
<p>Also see
{@link android.speech.RecognizerIntent#EXTRA_LANGUAGE_MODEL} for more
information.</p></dd>
+
<dt><code>android:voicePromptText</code></dt>
<dd><em>String</em>. An additional message to display in the voice input dialog.</dd>
+
<dt><code>android:voiceLanguage</code></dt>
<dd><em>String</em>. The spoken language to be expected, expressed as the string value of
-a constants in {@link java.util.Locale} (for example, {@code "de"} for German or {@code "fr"} for
-French). This is only needed if it is different from the current value of {@link
+a constants in {@link java.util.Locale} (such as {@code "de"} for German or {@code "fr"} for
+French). This is needed only if it is different from the current value of {@link
java.util.Locale#getDefault() Locale.getDefault()}.</dd>
+
<dt><code>android:voiceMaxResults</code></dt>
<dd><em>Integer</em>. Forces the maximum number of results to return,
- including the "best" result which will always be provided as the {@link
+ including the "best" result which is always provided as the {@link
android.content.Intent#ACTION_SEARCH} Intent's primary
query. Must be 1 or greater. Use {@link android.speech.RecognizerIntent#EXTRA_RESULTS} to
get the results from the Intent.
- If not provided, the recognizer will choose how many results to return.</dd>
+ If not provided, the recognizer chooses how many results to return.</dd>
</dl>
</dd> <!-- end searchable element -->
+
<dt id="actionkey-element"><code>&lt;actionkey&gt;</code></dt>
- <dd>Defines a shortcut key for a search action, in order to provide special behaviors at the touch
-of a button, based on the current query or focused suggestion. ​For example, the Contacts
-application enables the device call key for suggestions. So, when
-the user focuses on a search suggestion using the directional controls and then presses the call
-key, the application will immediately initiate a phone call to the suggested contact.
+ <dd>Defines a device key and behavior for a search action. A search action provides a
+special behavior at the touch of a button on the device, based on the current query or focused
+suggestion. For example, the Contacts application provides a search action to initiate a phone call
+to the currenly focused contact suggestion at the press of the CALL button.
<p>Not all action keys are available on every device, and not
all keys are allowed to be overriden in this way. For example, the "Home" key cannot be used and
must always return to the home screen. Also be sure not to define an action
key for a key that's needed for typing a search query. This essentially limits the
available and reasonable action keys to the call button and menu button. Also note that action
keys are not generally discoverable, so you should not provide them as a core user feature.</p>
+ <p>You must define the <code>android:keycode</code> to define the key and at least one of the
+other three attributes in order to define the search action.</p>
<p class="caps">attributes:</p>
<dl class="atn-list">
<dt><code>android:keycode</code></dt>
- <dd><em>String</em>. <strong>Required</strong>. A key code from {@link
+ <dd><em>String</em>. (Required.) A key code from {@link
android.view.KeyEvent} that represents the action key
- you wish to respond to (for example {@code "KEYCODE_CALL"}). This will be added to the
- {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} Intent that is passed to your
- searchable Activity. To examine the key code, use
- {@link android.content.Intent#getIntExtra getIntExtra(SearchManager.ACTION_KEY)}.
- In addition to the key code, you must also provide one or more of
- the action specifier attributes below. Not all action keys
-are actually supported using this mechanism, as many of them are used for typing,
- navigation, or system functions. Note that although each of the action message elements are
-optional, at least one must be present for the action key to have any effect.</dd>
+ you wish to respond to (for example {@code "KEYCODE_CALL"}). This is added to the
+ {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} Intent that is passed to your
+ searchable Activity. To examine the key code, use
+ {@link android.content.Intent#getIntExtra getIntExtra(SearchManager.ACTION_KEY)}. Not all
+keys are supported for a search action, as many of them are used for typing, navigation, or system
+functions.</dd>
+
<dt><code>android:queryActionMsg</code></dt>
<dd><em>String</em>. An action message to be sent if the action key is pressed while the
-user is simply entering query text. This will be added to the
- {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} Intent that is
- passed to your searchable Activity. To examine the string, use
+user is entering query text. This is added to the
+ {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH} Intent that the Search Manager
+ passes to your searchable Activity. To examine the string, use
{@link android.content.Intent#getStringExtra
getStringExtra(SearchManager.ACTION_MSG)}.</dd>
+
<dt><code>android:suggestActionMsg</code></dt>
<dd><em>String</em>. An action message to be sent if the action key is pressed while a
- suggestion is focused. This will be added to the
- Intent that is passed to your searchable Activity (using the action you've defined for
- the suggestion). To examine the string,
+ suggestion is in focus. This is added to the
+ Intent that that the Search Manager passes to your searchable Activity (using the action
+you've defined for the suggestion). To examine the string,
use {@link android.content.Intent#getStringExtra
- getStringExtra(SearchManager.ACTION_MSG)}. Note that this should only be used if all your
+ getStringExtra(SearchManager.ACTION_MSG)}. This should only be used if all your
suggestions support this action key. If not all suggestions can handle the same action key, then
you must instead use the following {@code android:suggestActionMsgColumn} attribute.</dd>
+
<dt><code>android:suggestActionMsgColumn</code></dt>
<dd><em>String</em>. The name of the column in your content provider that defines the
-action message for this action key, which is to be sent if the action key is pressed while a
- suggestion is focused. This attribute lets you control the
+action message for this action key, which is to be sent if the user presses the action key while a
+ suggestion is in focus. This attribute lets you control the
action key on a suggestion-by-suggestion basis, because, instead of using the {@code
android:suggestActionMsg} attribute to define the action message for all suggestions, each entry in
-your content provider provides its own action message. First, you must define a column in your
+your content provider provides its own action message.
+ <p>First, you must define a column in your
content provider for each suggestion to provide an action message, then provide the name of that
-column in this attribute. The search manager will look at your suggestion cursor,
- using the string provided here in order to select your action message column, and
- then select the action message string from the cursor. That string will be added to the
- Intent that is passed to your searchable Activity (using the action you've defined for
- suggestions). To examine the string, use {@link
+column in this attribute. The Search Manager looks at your suggestion cursor,
+ using the string provided here to select your action message column, and
+ then select the action message string from the Cursor. That string is added to the
+ Intent that the Search Manager passes to your searchable Activity (using the action you've
+defined for suggestions). To examine the string, use {@link
android.content.Intent#getStringExtra getStringExtra(SearchManager.ACTION_MSG)}. If the data
-does not exist for the selected suggestion, the action key will be ignored.</dd>
+does not exist for the selected suggestion, the action key is ignored.</dd>
</dl>
</dd><!-- end action key -->
</dl>
diff --git a/docs/html/guide/topics/ui/menus.jd b/docs/html/guide/topics/ui/menus.jd
index cf3c7de..b4e467c 100644
--- a/docs/html/guide/topics/ui/menus.jd
+++ b/docs/html/guide/topics/ui/menus.jd
@@ -5,198 +5,324 @@ parent.link=index.html
<div id="qv-wrapper">
<div id="qv">
- <h2>Key classes</h2>
- <ol>
- <li>{@link android.view.Menu}</li>
- <li>{@link android.view.ContextMenu}</li>
- <li>{@link android.view.SubMenu}</li>
- </ol>
<h2>In this document</h2>
<ol>
- <li><a href="#options-menu">Options Menu</a></li>
- <li><a href="#context-menu">Context Menu</a></li>
- <li><a href="#submenu">Submenu</a></li>
- <li><a href="#xml">Define Menus in XML</a></li>
- <li><a href="#features">Menu Features</a>
+ <li><a href="#xml">Defining Menus</a></li>
+ <li><a href="#Inflating">Inflating a Menu Resource</a>
+ <li><a href="#options-menu">Creating an Options Menu</a>
+ <ol>
+ <li><a href="#ChangingTheMenu">Changing the menu when it opens</a></li>
+ </ol>
+ </li>
+ <li><a href="#context-menu">Creating a Context Menu</a></li>
+ <li><a href="#submenu">Creating a Submenu</a></li>
+ <li><a href="#features">Other Menu Features</a>
<ol>
<li><a href="#groups">Menu groups</a></li>
<li><a href="#checkable">Checkable menu items</a></li>
<li><a href="#shortcuts">Shortcut keys</a></li>
- <li><a href="#intents">Menu item intents</a></li>
+ <li><a href="#intents">Intents for menu items</a></li>
</ol>
</li>
</ol>
+
+ <h2>Key classes</h2>
+ <ol>
+ <li>{@link android.view.Menu}</li>
+ <li>{@link android.view.MenuItem}</li>
+ <li>{@link android.view.ContextMenu}</li>
+ <li>{@link android.view.SubMenu}</li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}guide/topics/resources/menu-resource.html">Menu Resource</a></li>
+ </ol>
</div>
</div>
-<p>Menus are an important part of any application. They provide familiar interfaces
-that reveal application functions and settings. Android offers an easy programming interface
-for developers to provide standardized application menus for various situations.</p>
+<p>Menus are an important part of an application that provide a familiar interface for the user
+to access application functions and settings. Android offers an easy programming interface
+for you to provide application menus in your application.</p>
-<p>Android offers three fundamental types of application menus:</p>
+<p>Android provides three types of application menus:</p>
<dl>
<dt><strong>Options Menu</strong></dt>
- <dd>This is the primary set of menu items for an Activity. It is revealed by pressing
- the device MENU key. Within the Options Menu are two groups of menu items:
+ <dd>The primary menu for an Activity, which appears when the user presses
+ the device MENU key. Within the Options Menu are two groups:
<dl style="margin-top:1em">
<dt><em>Icon Menu</em></dt>
- <dd>This is the collection of items initially visible at the bottom of the screen
+ <dd>The menu items visible at the bottom of the screen
at the press of the MENU key. It supports a maximum of six menu items.
These are the only menu items that support icons and the only menu items that <em>do not</em> support
checkboxes or radio buttons.</dd>
<dt><em>Expanded Menu</em></dt>
- <dd>This is a vertical list of items exposed by the "More" menu item from the Icon Menu.
- It exists only when the Icon Menu becomes over-loaded and is comprised of the sixth
- Option Menu item and the rest.</dd>
+ <dd>The vertical list of menu items exposed by the "More" menu item in the Icon Menu.
+ When the Icon Menu is full, the expanded menu is comprised of the sixth
+ menu item and the rest.</dd>
</dl>
</dd>
<dt><strong>Context Menu</strong></dt>
- <dd>This is a floating list of menu items that may appear when you perform a long-press on a View
- (such as a list item). </dd>
+ <dd>A floating list of menu items that appears when the user performs a long-press on a View.
+</dd>
<dt><strong>Submenu</strong></dt>
- <dd>This is a floating list of menu items that is revealed by an item in the Options Menu
- or a Context Menu. A Submenu item cannot support nested Submenus. </dd>
+ <dd>A floating list of menu items that the user opens by pressing a menu item in the Options
+Menu or a context menu. A submenu item cannot support a nested submenu. </dd>
</dl>
-<h2 id="options-menu">Options Menu</h2>
-<img align="right" src="{@docRoot}images/options_menu.png" />
-<p>The Options Menu is opened by pressing the device MENU key.
-When opened, the Icon Menu is displayed, which holds the first six menu items.
-If more than six items are added to the Options Menu, then those that can't fit
-in the Icon Menu are revealed in the Expanded Menu, via the "More" menu item. The Expanded Menu
-is automatically added when there are more than six items.</p>
-<p>The Options Menu is where you should include basic application functions
-and any necessary navigation items (e.g., to a home screen or application settings).
-You can also add <a href="#submenu">Submenus</a> for organizing topics
-and including extra menu functionality.</p>
-
-<p>When this menu is opened for the first time,
-the Android system will call the Activity <code>{@link android.app.Activity#onCreateOptionsMenu(Menu)
-onCreateOptionsMenu()}</code> callback method. Override this method in your Activity
-and populate the {@link android.view.Menu} object given to you. You can populate the menu by
-inflating a menu resource that was <a href="#xml">defined in XML</a>, or by calling
-<code>{@link android.view.Menu#add(CharSequence) add()}</code>
-for each item you'd like in the menu. This method adds a {@link android.view.MenuItem}, and returns the
-newly created object to you. You can use the returned MenuItem to set additional properties like
-an icon, a keyboard shortcut, an intent, and other settings for the item.</p>
-
-<p>There are multiple <code>{@link android.view.Menu#add(CharSequence) add()}</code> methods.
-Usually, you'll want to use one that accepts an <var>itemId</var> argument.
-This is a unique integer that allows you to identify the item during a callback.</p>
-
-<p>When a menu item is selected from the Options Menu, you will receive a callback to the
-<code>{@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}</code>
-method of your Activity. This callback passes you the
-<code>MenuItem</code> that has been selected. You can identify the item by requesting the
-<var>itemId</var>, with <code>{@link android.view.MenuItem#getItemId() getItemId()}</code>,
-which returns the integer that was assigned with the <code>add()</code> method. Once you identify
-the menu item, you can take the appropriate action.</p>
-
-<p>Here's an example of this procedure, inside an Activity, wherein we create an
-Options Menu and handle item selections:</p>
+<h2 id="xml">Defining Menus</h2>
+
+<p>Instead of instantiating {@link android.view.Menu} objects in your application code, you should
+define a menu and all its items in an XML <a
+href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>, then inflate the menu
+resource (load it as a programmable object) in your application code. Defining your menus in XML is
+a good practice because it separates your interface design from your application code (the same as
+when you <a href="{@docRoot}guide/topics/ui/declaring-layout.html">define your Activity
+layout</a>).</p>
+
+<p>To define a menu, create an XML file inside your project's <code>res/menu/</code>
+directory and build the menu with the following elements:</p>
+<dl>
+ <dt><code>&lt;menu></code></dt>
+ <dd>Creates a {@link android.view.Menu}, which is a container for menu items. It must be
+the root node and holds one or more of the following elements. You can also nest this element
+in an {@code &lt;item&gt;} to create a submenu.</dd>
+ <dt><code>&lt;item></code></dt>
+ <dd>Creates a {@link android.view.MenuItem}, which represents a single item in a menu.</dd>
+ <dt><code>&lt;group></code></dt>
+ <dd>An optional, invisible container for {@code &lt;item&gt;} elements. It allows you to
+categorize menu items so they share properties such as active state and visibility. See <a
+href="#groups">Menu groups</a>.</dd>
+</dl>
+<p>For example, here is a file in <code>res/menu/</code> named <code>game_menu.xml</code>:</p>
<pre>
-/* Creates the menu items */
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;item android:id="@+id/new_game"
+ android:icon="@drawable/ic_new_game"
+ android:title="@string/new_game" /&gt;
+ &lt;item android:id="@+id/quit"
+ android:icon="@drawable/ic_quit"
+ android:title="@string/quit" /&gt;
+&lt;/menu&gt;
+</pre>
+
+<p>This example defines a menu with two menu items. Each item includes the attributes:</p>
+<dl>
+ <dt>{@code android:id}</dt>
+ <dd>A resource ID that's unique to the item so that the application can recognize the item when
+the user selects it.</dd>
+ <dt>{@code android:icon}</dt>
+ <dd>A drawable resource that is the icon visible to the user.</dd>
+ <dt>{@code android:title}</dt>
+ <dd>A string resource that is the title visible to the user.</dd>
+</dl>
+
+<p>For more about the XML syntax and attributes for a menu resource, see the <a
+href="{@docRoot}guide/topics/resources/menu-resource.html">Menu Resource</a> reference.</p>
+
+
+<h2 id="Inflating">Inflating a Menu Resource</h2>
+
+<p>You can inflate your menu resource (convert the XML resource into a programmable object) using
+{@link android.view.MenuInflater#inflate(int,Menu) MenuInflater.inflate()}. For
+example, the following code inflates the <code>game_menu.xml</code> file defined above during the
+{@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} callback method, to be
+used for the Options Menu:</p>
+
+<pre>
+&#64;Override
public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, MENU_NEW_GAME, 0, "New Game");
- menu.add(0, MENU_QUIT, 0, "Quit");
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.game_menu, menu);
return true;
}
+</pre>
+
+<p>The {@link android.app.Activity#getMenuInflater()} method returns a {@link
+android.view.MenuInflater} for the Activity. With this object, you can call {@link
+android.view.MenuInflater#inflate(int,Menu) inflate()}, which inflates a menu resource into a
+{@link android.view.Menu} object. In this example, the menu resource defined by
+<code>game_menu.xml</code>
+is inflated into the {@link android.view.Menu} that was passed into {@link
+android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()}. (This callback method for
+creating an option menu is discussed more in the next section.)</p>
+
+
+
+<h2 id="options-menu">Creating an Options Menu</h2>
+
+<div class="figure" style="width:200px">
+ <img src="{@docRoot}images/options_menu.png" height="300" alt="" />
+ <p class="img-caption"><strong>Figure 1.</strong> Screenshot of an Options Menu.</p>
+</div>
+
+
+<p>The Options Menu is where you should include basic application functions
+and necessary navigation items (for example, a button
+to open application settings). The user
+can open the Options Menu with the device MENU key.
+Figure 1 shows a screenshot of an Options Menu.</p>
+
+<p>When opened, the first visible portion of the Options Menu is called the Icon Menu. It
+holds the first six menu items.
+If you add more than six items to the Options Menu, Android places the sixth item and those after it
+into the Expanded Menu, which the user can open with the "More" menu item.</p>
+
+<p>When the user opens the Options Menu for the first time, Android calls your Activity's
+{@link android.app.Activity#onCreateOptionsMenu(Menu)
+onCreateOptionsMenu()} method. Override this method in your Activity
+and populate the {@link android.view.Menu} that is passed into the method. Populate the
+{@link android.view.Menu} by inflating a menu resource as described in <a
+href="#Inflating">Inflating a Menu Resource</a>. (You can
+also populate the menu in code, using {@link android.view.Menu#add(int,int,int,int)
+add()} to add menu items.)</p>
+
+<p>When the user selects a menu item from the Options Menu, the system calls your Activity's
+{@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}
+method. This method passes the
+{@link android.view.MenuItem} that the user selected. You can identify the menu item by calling
+{@link android.view.MenuItem#getItemId()}, which returns the unique ID for the menu
+item (defined by the {@code android:id} attribute in the menu resource or with an integer passed
+to the {@link android.view.Menu#add(int,int,int,int) add()} method). You can match this ID
+against known menu items and perform the appropriate action.</p>
+
+<p>For example:</p>
-/* Handles item selections */
+<pre>
+&#64;Override
public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle item selection
switch (item.getItemId()) {
- case MENU_NEW_GAME:
+ case R.id.new_game:
newGame();
return true;
- case MENU_QUIT:
+ case R.id.quit:
quit();
return true;
+ default:
+ return super.onOptionsItemSelected(item);
}
- return false;
}
</pre>
-<p>The <code>add()</code> method used in this sample takes four arguments:
-<var>groupId</var>, <var>itemId</var>, <var>order</var>, and <var>title</var>.
-The <var>groupId</var> allows you to associate this menu item with a group of other items
-(more about <a href="#groups">Menu groups</a>, below) &mdash; in
-this example, we ignore it. <var>itemId</var> is a unique integer that we give the
-MenuItem so that can identify it in the next callback. <var>order</var> allows us to
-define the display order of the item &mdash; by default, they are displayed by the
-order in which we add them. <var>title</var> is, of course, the name that goes on the
-menu item (this can also be a
-<a href="{@docRoot}guide/topics/resources/available-resources.html#stringresources">string resource</a>,
-and we recommend you do it that way for easier localization).</p>
-
-<p class="note"><strong>Tip:</strong>
-If you have several menu items that can be grouped together with a title,
-consider organizing them into a <a href="#submenu">Submenu</a>.</p>
-
-<h3>Adding icons</h3>
-<p>Icons can also be added to items that appears in the Icon Menu with
-<code>{@link android.view.MenuItem#setIcon(Drawable) setIcon()}</code>. For example:</p>
-<pre>
-menu.add(0, MENU_QUIT, 0, "Quit")
- .setIcon(R.drawable.menu_quit_icon);</pre>
-
-<h3>Modifying the menu</h3>
-<p>If you want to sometimes re-write the Options Menu as it is opened, override the
-<code>{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()}</code> method, which is
-called each time the menu is opened. This will pass you the Menu object, just like the
-<code>onCreateOptionsMenu()</code> callback. This is useful if you'd like to add or remove
-menu options depending on the current state of an application or game.</p>
+<p>In this example, {@link android.view.MenuItem#getItemId()} queries the ID for the selected menu
+item and the switch statement compares the ID against the resource IDs that were assigned to menu
+items in the XML resource. When a switch case successfully handles the item, it
+returns "true" to indicate that the item selection was handled. Otherwise, the default statement
+passes the menu item to the super class in
+case it can handle the item selected. (If you've directly extended the {@link android.app.Activity}
+class, then the super class returns "false", but it's a good practice to
+pass unhandled menu items to the super class instead of directly returning "false".)</p>
+
+<p class="note"><strong>Tip:</strong> If your application contains multiple activities and
+some of them provide the same Options Menu, consider creating
+an Activity that implements nothing except the {@link android.app.Activity#onCreateOptionsMenu(Menu)
+onCreateOptionsMenu()} and {@link android.app.Activity#onOptionsItemSelected(MenuItem)
+onOptionsItemSelected()} methods. Then extend this class for each Activity that should share the
+same Options Menu. This way, you have to manage only one set of code for handling menu
+actions and each decendent class inherits the menu behaviors.<br/><br/>
+If you want to add menu items to one of your decendent activities,
+override {@link android.app.Activity#onCreateOptionsMenu(Menu)
+onCreateOptionsMenu()} in that Activity. Call {@code super.onCreateOptionsMenu(menu)} so the
+original menu items are created, then add new menu items with {@link
+android.view.Menu#add(int,int,int,int) menu.add()}. You can also override the super class's
+behavior for individual menu items.</p>
+
+
+<h3 id="ChangingTheMenu">Changing the menu when it opens</h3>
+
+<p>The {@link android.app.Activity#onCreateOptionsMenu(Menu) onCreateOptionsMenu()} method is
+called only the first time the Options Menu is opened. The system keeps and re-uses the {@link
+android.view.Menu} you define in this method until your Activity is destroyed. If you want to change
+the Options Menu each time it opens, you must override the
+{@link android.app.Activity#onPrepareOptionsMenu(Menu) onPrepareOptionsMenu()} method. This passes
+you the {@link android.view.Menu} object as it currently exists. This is useful if you'd like to
+remove, add, disable, or enable menu items depending on the current state of your application.</p>
<p class="note"><strong>Note:</strong>
-When changing items in the menu, it's bad practice to do so based on the currently selected item.
-Keep in mind that, when in touch mode, there will not be a selected (or focused) item. Instead, you
-should use a <a href="#context-menu">Context Menu</a> for such behaviors, when you want to provide
-functionality based on a particular item in the UI.</p>
-
-
-<h2 id="context-menu">Context Menu</h2>
-<p>The Android context menu is similar, in concept, to the menu revealed with a "right-click" on a PC.
-When a view is registered to a context menu,
-performing a "long-press" (press and hold for about two seconds) on the object
-will reveal a floating menu that provides functions relating to that item.
-Context menus can be registered to any View object,
-however, they are most often used for items in a
-{@link android.widget.ListView}, which helpfully indicates the presence of the context menu
-by transforming the background color of the ListView item when pressed.
-(The items in the phone's contact list offer an example of this feature.)
-</p>
-
-<p class="note"><strong>Note:</strong> Context menu items do not support icons or shortcut keys.</p>
-
-<p>To create a context menu, you must override the Activity's context menu callback methods:
-<code>{@link android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo) onCreateContextMenu()}</code> and
-<code>{@link android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()}</code>.
-Inside the <code>onCreateContextMenu()</code> callback method, you can add menu items using one of the
-<code>{@link android.view.Menu#add(CharSequence) add()}</code> methods, or by
-inflating a menu resource that was <a href="#xml">defined in XML</a>.
-Then, register a {@link android.view.ContextMenu} for the View, with
-<code>{@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()}</code>.</p>
-
-<p>For example, here is some code that can be used with the
-<a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad application</a>
-to add a context menu for each note in the list:</p>
+You should never change items in the Options Menu based on the {@link android.view.View} currently
+in focus. When in touch mode (when the user is not using a trackball or d-pad), Views
+cannot take focus, so you should never use focus as the basis for modifying
+items in the Options Menu. If you want to provide menu items that are context-sensitive to a {@link
+android.view.View}, use a <a href="#context-menu">Context Menu</a>.</p>
+
+
+
+<h2 id="context-menu">Creating a Context Menu</h2>
+
+<p>A context menu is conceptually similar to the menu displayed when the user performs a
+"right-click" on a PC. You should use a context menu to provide the user access to
+actions that pertain to a specific item in the user interface. On Android, a context menu is
+displayed when the user performs a "long press" (press and hold) on an item.</p>
+
+<p>You can create a context menu for any View, though context menus are most often used for items in
+a {@link android.widget.ListView}. When the user performs a long-press on an item in a ListView and
+the list is registered to provide a context menu, the list item signals to the user that a context
+menu is available by animating its background color&mdash;it transitions from
+orange to white before opening the context menu. (The Contacts application demonstrates this
+feature.)</p>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h3>Register a ListView</h3>
+<p>If your Activity uses a {@link android.widget.ListView} and
+you want all list items to provide a context menu, register all items for a context
+menu by passing the {@link android.widget.ListView} to {@link
+android.app.Activity#registerForContextMenu(View) registerForContextMenu()}. For
+example, if you're using a {@link android.app.ListActivity}, register all list items like this:</p>
+<p><code>registerForContextMenu({@link android.app.ListActivity#getListView()});</code></p>
+</div>
+</div>
+
+<p>In order for a View to provide a context menu, you must "register" the view for a context
+menu. Call {@link android.app.Activity#registerForContextMenu(View) registerForContextMenu()} and
+pass it the {@link android.view.View} you want to give a context menu. When this View then
+receives a long-press, it displays a context menu.</p>
+
+<p>To define the context menu's appearance and behavior, override your Activity's context menu
+callback methods, {@link android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo)
+onCreateContextMenu()} and
+{@link android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()}.</p>
+
+<p>For example, here's an {@link
+android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo)
+onCreateContextMenu()} that uses the {@code context_menu.xml} menu resource:</p>
<pre>
+&#64;Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- menu.add(0, EDIT_ID, 0, "Edit");
- menu.add(0, DELETE_ID, 0, "Delete");
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.context_menu, menu);
}
+</pre>
+
+<p>{@link android.view.MenuInflater} is used to inflate the context menu from a <a
+href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>. (You can also use
+{@link android.view.Menu#add(int,int,int,int) add()} to add menu items.) The callback method
+parameters include the {@link android.view.View}
+that the user selected and a {@link android.view.ContextMenu.ContextMenuInfo} object that provides
+additional information about the item selected. You might use these parameters to determine
+which context menu should be created, but in this example, all context menus for the Activity are
+the same.</p>
+
+<p>Then when the user selects an item from the context menu, the system calls {@link
+android.app.Activity#onContextItemSelected(MenuItem) onContextItemSelected()}. Here is an example
+of how you can handle selected items:</p>
+<pre>
+&#64;Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
- case EDIT_ID:
+ case R.id.edit:
editNote(info.id);
return true;
- case DELETE_ID:
+ case R.id.delete:
deleteNote(info.id);
return true;
default:
@@ -205,285 +331,276 @@ public boolean onContextItemSelected(MenuItem item) {
}
</pre>
-<p>In <code>onCreateContextMenu()</code>, we are given not only the ContextMenu to
-which we will add {@link android.view.MenuItem}s, but also the {@link android.view.View}
-that was selected and a {@link android.view.ContextMenu.ContextMenuInfo ContextMenuInfo} object,
-which provides additional information about the object that was selected.
-In this example, nothing special is done in <code>onCreateContextMenu()</code> &mdash; just
-a couple items are added as usual. In the <code>onContextItemSelected()</code>
-callback, we request the {@link android.widget.AdapterView.AdapterContextMenuInfo AdapterContextMenuInfo}
-from the {@code MenuItem}, which provides information about the currently selected item.
-All we need from
-this is the list ID for the selected item, so whether editing a note or deleting it,
-we find the ID with the {@code AdapterContextMenuInfo.info} field of the object. This ID
-is passed to the <code>editNote()</code> and <code>deleteNote()</code> methods to perform
-the respective action.</p>
-
-<p>Now, to register this context menu for all the items in a {@link android.widget.ListView},
-we pass the entire {@code ListView} to the
-<code>{@link android.app.Activity#registerForContextMenu(View)}</code> method:</p>
-
-<pre>registerForContextMenu(getListView());</pre>
-<p>Remember, you can pass any View object to register a context menu. Here,
-<code>{@link android.app.ListActivity#getListView()}</code> returns the ListView
-object used in the Notepad application's {@link android.app.ListActivity}. As such, each item
-in the list is registered to this context menu.</p>
-
-
-
-<h2 id="submenu">Submenus</h2>
-<p>A sub menu can be added within any menu, except another sub menu.
-These are very useful when your application has a lot of functions that may be
-organized in topics, like the items in a PC application's menu bar (File, Edit, View, etc.).</p>
-
-<p>A sub menu is created by adding it to an existing {@link android.view.Menu}
-with <code>{@link android.view.Menu#addSubMenu(CharSequence) addSubMenu()}</code>.
-This returns a {@link android.view.SubMenu} object (an extension of {@link android.view.Menu}).
-You can then add additional items to this menu, with the normal routine, using
-the <code>{@link android.view.Menu#add(CharSequence) add()}</code> methods. For example:</p>
+<p>The structure of this code is similar to the example for <a href="#options-menu">Creating an
+Options Menu</a>, in which {@link android.view.MenuItem#getItemId()} queries the ID for the selected
+menu item and a switch statement matches the item to the IDs that are defined in the menu resource.
+And like the options menu example, the default statement calls the super class in case it
+can handle menu items not handled here, if necessary.</p>
-<pre>
-public boolean onCreateOptionsMenu(Menu menu) {
- boolean result = super.onCreateOptionsMenu(menu);
+<p>In this example, the selected item is an item from a {@link android.widget.ListView}. To
+perform an action on the selected item, the application needs to know the list
+ID for the selected item (it's position in the ListView). To get the ID, the application calls
+{@link android.view.MenuItem#getMenuInfo()}, which returns a {@link
+android.widget.AdapterView.AdapterContextMenuInfo} object that includes the list ID for the
+selected item in the {@link android.widget.AdapterView.AdapterContextMenuInfo#id id} field. The
+local methods <code>editNote()</code> and <code>deleteNote()</code> methods accept this list ID to
+perform an action on the data specified by the list ID.</p>
- SubMenu fileMenu = menu.addSubMenu("File");
- SubMenu editMenu = menu.addSubMenu("Edit");
- fileMenu.add("new");
- fileMenu.add("open");
- fileMenu.add("save");
- editMenu.add("undo");
- editMenu.add("redo");
+<p class="note"><strong>Note:</strong> Items in a context menu do not support icons or shortcut
+keys.</p>
- return result;
-}
-</pre>
-<p>Callbacks for items selected in a sub menu are made to the parent menu's callback method.
-For the example above, selections in the sub menu will be handled by the
-<code>onOptionsItemSelected()</code> callback.</p>
-<p>You can also add Submenus when you <a href="#xml">define the parent menu in XML</a>.</p>
-<h2 id="xml">Define Menus in XML</h2>
-<p>Just like Android UI layouts, you can define application menus in XML, then inflate them
-in your menu's <code>onCreate...()</code> callback method. This makes your application code cleaner and
-separates more interface design into XML, which is easier to visualize.</p>
+<h2 id="submenu">Creating Submenus</h2>
-<p>To start, create a new folder in your project <code>res/</code> directory called <code>menu</code>.
-This is where you should keep all XML files that define your application menus.</p>
+<p>A submenu is a menu that the user can open by selecting an item in another menu. You can add a
+submenu to any menu (except a submenu). Submenus are useful when your application has a lot of
+functions that can be organized into topics, like items in a PC application's menu bar (File, Edit,
+View, etc.).</p>
-<p>In a menu XML layout, there are
-three valid elements: <code>&lt;menu></code>, <code>&lt;group></code> and <code>&lt;item></code>. The
-<code>item</code> and <code>group</code> elements must be children of a <code>menu</code>, but <code>item</code>
-elements may also be the children of a <code>group</code>, and another <code>menu</code> element may be the child
-of an <code>item</code> (to create a Submenu). Of course, the root node of any file
-must be a <code>menu</code> element.</p>
+<p>When creating your <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu
+resource</a>, you can create a submenu by adding a {@code &lt;menu&gt;} element as the child of an
+{@code &lt;item&gt;}. For example:</p>
-<p>As an example, we'll define the same menu created in the <a href="#options-menu">Options Menu</a> section,
-above. We start with an XML file named <code>options_menu.xml</code> inside the <code>res/menu/</code> folder:</p>
<pre>
-&lt;menu xmlns:android="http://schemas.android.com/apk/res/android">
- &lt;item android:id="@+id/new_game"
- android:title="New Game" />
- &lt;item android:id="@+id/quit"
- android:title="Quit" />
-&lt;/menu>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;item android:id="@+id/file"
+ android:icon="@drawable/file"
+ android:title="@string/file" &gt;
+ &lt;!-- "file" submenu --&gt;
+ &lt;menu"&gt;
+ &lt;item android:id="@+id/new"
+ android:title="@string/new" /&gt;
+ &lt;item android:id="@+id/open"
+ android:title="@string/open" /&gt;
+ &lt;/menu&gt;
+ &lt;/item&gt;
+&lt;/menu&gt;
</pre>
-<p>Then, in the <code>onCreateOptionsMenu()</code> method, we inflate this resource using
-<code>{@link android.view.MenuInflater#inflate(int,Menu) MenuInflater.inflate()}</code>:</p>
-<pre>
-public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.options_menu, menu);
- return true;
-}
-</pre>
+<p>When the user selects an item from a submenu, the parent menu's respective on-item-selected
+callback method receives the event. For instance, if the above menu is applied as an Options Menu,
+then the {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} method
+is called when a submenu item is selected.</p>
-<p>The <code>{@link android.app.Activity#getMenuInflater()}</code> method returns the {@link android.view.MenuInflater}
-for our activity's context. We then call <code>{@link android.view.MenuInflater#inflate(int,Menu) inflate()}</code>,
-passing it a pointer to our menu resource and the Menu object given by the callback.</code></p>
+<p>You can also use {@link android.view.Menu#addSubMenu(int,int,int,int) addSubMenu()} to
+dynamically add a {@link android.view.SubMenu} to an existing {@link android.view.Menu}. This
+returns the new {@link android.view.SubMenu} object, to which you can add
+submenu items, using {@link android.view.Menu#add(int,int,int,int) add()}</p>
-<p>While this small sample may seem like more effort, compared to creating the menu items in the
-<code>onCreateOptionsMenu()</code> method, this will save a lot of trouble when dealing with more items
-and it keeps your application code clean.</p>
-<p>You can define <a href="#groups">menu groups</a> by wrapping <code>item</code> elements in a <code>group</code>
-element, and create Submenus by nesting another <code>menu</code> inside an <code>item</code>.
-Each element also supports all the necessary attributes to control features like shortcut keys,
-checkboxes, icons, and more. To learn about these attributes and more about the XML syntax, see the Menus
-topic in the <a href="{@docRoot}guide/topics/resources/available-resources.html#menus">Available
-Resource Types</a> document.</p>
-<h2 id="features">Menu Features</h2>
-<p>Here are some other features that can be applied to most menu items.</p>
+<h2 id="features">Other Menu Features</h2>
+
+<p>Here are some other features that you can apply to most menu items.</p>
<h3 id="groups">Menu groups</h3>
-<p>When adding new items to a menu, you can optionally include each item in a group.
-A menu group is a collection of menu items that can share certain traits, like
-whether they are visible, enabled, or checkable.</p>
-
-<p>A group is defined by an integer (or a resource id, in XML). A menu item is added to the group when it is
-added to the menu, using one of the <code>add()</code> methods that accepts a <var>groupId</var>
-as an argument, such as <code>{@link android.view.Menu#add(int,int,int,int)}</code>.</p>
-
-<p>You can show or hide the entire group with
-<code>{@link android.view.Menu#setGroupVisible(int,boolean) setGroupVisible()}</code>;
-enable or disable the group with
-<code>{@link android.view.Menu#setGroupEnabled(int,boolean) setGroupEnabled()}</code>;
-and set whether the items can be checkable with
-<code>{@link android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()}</code>.
-</p>
+
+<p>A menu group is a collection of menu items that share certain traits. With a group, you
+can:</p>
+<ul>
+ <li>Show or hide all items with {@link android.view.Menu#setGroupVisible(int,boolean)
+setGroupVisible()}</li>
+ <li>Enable or disable all items with {@link android.view.Menu#setGroupEnabled(int,boolean)
+setGroupEnabled()}</li>
+ <li>Specify whether all items are checkable with {@link
+android.view.Menu#setGroupCheckable(int,boolean,boolean) setGroupCheckable()}</li>
+</ul>
+
+<p>You can create a group by nesting {@code &lt;item&gt;} elements inside a {@code &lt;group&gt;}
+element in your menu resource or by specifying a group ID with the the {@link
+android.view.Menu#add(int,int,int,int) add()} method.</p>
+
+<p>Here's an example menu resource that includes a group:</p>
+
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;item android:id="@+id/item1"
+ android:icon="@drawable/item1"
+ android:title="@string/item1" /&gt;
+ &lt;!-- menu group --&gt;
+ &lt;group android:id="@+id/group1"&gt;
+ &lt;item android:id="@+id/groupItem1"
+ android:title="@string/groupItem1" /&gt;
+ &lt;item android:id="@+id/groupItem2"
+ android:title="@string/groupItem2" /&gt;
+ &lt;/group&gt;
+&lt;/menu&gt;
+</pre>
+
+<p>The items that are in the group appear the same as the first item that is not in a
+group&mdash;all three items in the menu are siblings. However, you can modify the traits of the two
+items in the group by referencing the group ID and using the methods listed above.</p>
+
<h3 id="checkable">Checkable menu items</h3>
-<img align="right" src="{@docRoot}images/radio_buttons.png" alt="" />
-<p>Any menu item can be used as an interface for turning options on and off. This can
-be indicated with a checkbox for stand-alone options, or radio buttons for groups of
-mutually exclusive options (see the screenshot, to the right).</p>
-<p class="note"><strong>Note:</strong> Menu items in the Icon Menu cannot
+<div class="figure" style="width:200px">
+ <img src="{@docRoot}images/radio_buttons.png" height="300" alt="" />
+ <p class="img-caption"><strong>Figure 2.</strong> Screenshot of checkable menu items</p>
+</div>
+
+<p>A menu can be useful as an interface for turning options on and off, using a checkbox for
+stand-alone options, or radio buttons for groups of
+mutually exclusive options. Figure 2 shows a submenu with items that are checkable with radio
+buttons.</p>
+
+<p class="note"><strong>Note:</strong> Menu items in the Icon Menu (from the Options Menu) cannot
display a checkbox or radio button. If you choose to make items in the Icon Menu checkable,
-then you must personally indicate the state by swapping the icon and/or text
-each time the state changes between on and off.</p>
+you must manually indicate the checked state by swapping the icon and/or text
+each time the state changes.</p>
+
+<p>You can define the checkable behavior for individual menu items using the {@code
+android:checkable} attribute in the {@code &lt;item&gt;} element, or for an entire group with
+the {@code android:checkableBehavior} attribute in the {@code &lt;group&gt;} element. For
+example, all items in this menu group are checkable with a radio button:</p>
-<p>To make a single item checkable, use the <code>{@link android.view.MenuItem#setCheckable(boolean)
-setCheckable()}</code> method, like so:</p>
<pre>
-menu.add(0, VIBRATE_SETTING_ID, 0, "Vibrate")
- .setCheckable(true);
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;menu xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+ &lt;group android:checkableBehavior="single"&gt;
+ &lt;item android:id="@+id/red"
+ android:title="@string/red" /&gt;
+ &lt;item android:id="@+id/blue"
+ android:title="@string/blue" /&gt;
+ &lt;/group&gt;
+&lt;/menu&gt;
</pre>
-<p>This will display a checkbox with the menu item (unless it's in the Icon Menu). When the item
-is selected, the <code>onOptionsItemSelected()</code> callback is called as usual. It is here that
-you must set the state of the checkbox. You can query the current state of the item with
-<code>{@link android.view.MenuItem#isChecked()}</code> and set the checked state with
-<code>{@link android.view.MenuItem#setChecked(boolean) setChecked()}</code>.
-Here's what this looks like inside the
-<code>onOptionsItemSelected()</code> callback:</p>
+
+<p>The {@code android:checkableBehavior} attribute accepts either:
+<dl>
+ <dt>{@code single}</dt>
+ <dd>Only one item from the group can be checked (radio buttons)</dd>
+ <dt>{@code all}</dt>
+ <dd>All items can be checked (checkboxes)</dd>
+ <dt>{@code none}</dt>
+ <dd>No items are checkable</dd>
+</dl>
+
+<p>You can apply a default checked state to an item using the {@code android:checked} attribute in
+the {@code &lt;item&gt;} element and change it in code with the {@link
+android.view.MenuItem#setChecked(boolean) setChecked()} method.</p>
+
+<p>When a checkable item is selected, the system calls your respective item-selected callback method
+(such as {@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}). It
+is here that you must set the state of the checkbox, because a checkbox or radio button does not
+change its state automatically. You can query the current state of the item (as it was before the
+user selected it) with {@link android.view.MenuItem#isChecked()} and then set the checked state with
+{@link android.view.MenuItem#setChecked(boolean) setChecked()}. For example:</p>
+
<pre>
-switch (item.getItemId()) {
-case VIBRATE_SETTING_ID:
- if (item.isChecked()) item.setChecked(false);
- else item.setChecked(true);
- return true;
-...
+&#64;Override
+public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.vibrate:
+ case R.id.dont_vibrate:
+ if (item.isChecked()) item.setChecked(false);
+ else item.setChecked(true);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
}
</pre>
-<p>To make a group of mutually exclusive radio button items, simply
-assign the same group ID to each menu item
-and call <code>{@link android.view.Menu#setGroupCheckable(int,boolean,boolean)
-setGroupCheckable()}</code>. In this case, you don't need to call <code>setCheckable()</code>
-on each menu items, because the group as a whole is set checkable. Here's an example of
-two mutually exclusive options in a Submenu:</p>
-<pre>
-SubMenu subMenu = menu.addSubMenu("Color");
-subMenu.add(COLOR_MENU_GROUP, COLOR_RED_ID, 0, "Red");
-subMenu.add(COLOR_MENU_GROUP, COLOR_BLUE_ID, 0, "Blue");
-subMenu.setGroupCheckable(COLOR_MENU_GROUP, true, true);
-</pre>
-<p>In the <code>setGroupCheckable()</code> method, the first argument is the group ID
-that we want to set checkable. The second argument is whether we want the group items
-to be checkable. The last one is whether we want each item to be exclusively checkable
-(if we set this <em>false</em>, then all the items will be checkboxes instead of radio buttons).
-When the group is set to be exclusive (radio buttons), each time a new item is selected,
-all other are automatically de-selected.</p>
-<p>
+<p>If you don't set the checked state this way, then the visible state of the item (the checkbox or
+radio button) will not
+change when the user selects it. When you do set the state, the Activity preserves the checked state
+of the item so that when the user opens the menu later, the checked state that you
+set is visible.</p>
<p class="note"><strong>Note:</strong>
-Checkable menu items are intended to be used only on a per-session basis and not saved to the device
-(e.g., the <em>Map mode</em> setting in the Maps application is not saved &mdash; screenshot above).
-If there are application settings that you would like to save for the user,
-then you should store the data using <a href="#{@docRoot}guide/topics/data/data-storage.html#pref">Preferences</a>,
-and manage them with a {@link android.preference.PreferenceActivity}.</p>
+Checkable menu items are intended to be used only on a per-session basis and not saved after the
+application is destroyed. If you have application settings that you would like to save for the user,
+you should store the data using <a
+href="#{@docRoot}guide/topics/data/data-storage.html#pref">Shared Preferences</a>.</p>
<h3 id="shortcuts">Shortcut keys</h3>
-<p>Quick access shortcut keys using letters and/or numbers can be added to menu items with
-<code>setAlphabeticShortcut(char)</code> (to set char shortcut), <code>setNumericShortcut(int)</code>
-(to set numeric shortcut),
-or <code>setShortcut(char,int)</code> (to set both)</code>. Case is <em>not</em> sensitive.
-For example:</p>
-<pre>
-menu.add(0, MENU_QUIT, 0, "Quit")
- .setAlphabeticShortcut('q');
-</pre>
-<p>Now, when the menu is open (or while holding the MENU key), pressing the "q" key will
-select this item.</p>
-<p>This shortcut key will be displayed as a tip in the menu item, below the menu item name
-(except for items in the Icon Menu).</p>
-<p class="note"><strong>Note:</strong> Shortcuts cannot be added to items in a Context Menu.</p>
-
-
-<h3 id="intents">Menu item intents</h3>
-<p>If you've read the <a href="{@docRoot}guide/topics/fundamentals.html">Application
-Fundamentals</a>, then you're at least a little familiar
-with Android Intents. These allow applications to bind with each other, share information,
-and perform user tasks cooperatively. Just like your application might fire an Intent to launch a web browser,
-an email client, or another Activity in your application,
-you can perform such actions from within a menu.
-There are two ways to do this: define an Intent and assign it to a single menu item, or
-define an Intent and allow Android to search the device for activities and dynamically add a
-menu item for each one that meets the Intent criteria.</p>
-
-<p>For more information on creating Intents and providing your application's services to other applications,
-read the <a href="/guide/topics/intents/intents-filters.html">Intents
-and Intent Filters</a> document.</p>
-
-<h4>Set an intent for a single menu item</h4>
-<p>If you want to offer a specific menu item that launches a new Activity, then you
-can specifically define an Intent for the menu item with the
-<code>{@link android.view.MenuItem#setIntent(Intent)
-setIntent()}</code> method.</p>
-
-<p>For example, inside the <code>{@link android.app.Activity#onCreateOptionsMenu(Menu)
-onCreateOptionsMenu()}</code> method, you can define a new menu item with an Intent like this:</p>
-<pre>
-MenuItem menuItem = menu.add(0, PHOTO_PICKER_ID, 0, "Select Photo");
-menuItem.setIntent(new Intent(this, PhotoPicker.class));
-</pre>
-<p>Android will automatically launch the Activity when the item is selected.</p>
-
-<p class="note"><strong>Note:</strong> This will not return a result to your Activity.
-If you wish to be returned a result, then do not use <code>setIntent()</code>.
-Instead, handle the selection as usual in the <code>onOptionsMenuItemSelected()</code>
-or <code>onContextMenuItemSelected()</code> callback and call
-<code>{@link android.app.Activity#startActivityForResult(Intent,int) startActivityForResult()}</code>.
-</p>
-
-<h4>Dynamically add intents</h4>
-
-<p>If there are potentially multiple activities that are relevant to your current
-Activity or selected item, then the application can dynamically add menu items that execute other
-services.</p>
-<p>During menu creation, define an Intent with the category <var>Intent.ALTERNATIVE_CATEGORY</var> and/or
-<var>Intent.SELECTED_ALTERNATIVE</var>, the MIME type currently selected (if any), and any other
-requirements, the same way as you would satisfy an intent filter to open a new
-Activity. Then call
-<code>{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[])
-addIntentOptions()}</code> to have Android search for any services meeting those requirements
-and add them to the menu for you. If there are no applications installed
-that satisfy the Intent, then no additional menu items are added.</p>
+<p>You can add quick-access shortcut keys using letters and/or numbers to menu items with the
+{@code android:alphabeticShortcut} and {@code android:numericShortcut} attributes in the {@code
+&lt;item&gt;} element. You can also use the methods {@link
+android.view.MenuItem#setAlphabeticShortcut(char)} and {@link
+android.view.MenuItem#setNumericShortcut(char)}. Shortcut keys are <em>not</em>
+case sensitive.</p>
+
+<p>For example, if you apply the "s" character as an alphabetic shortcut to a "save" menu item, then
+when the menu is open (or while the user holds the MENU key) and the user presses the "s" key,
+the "save" menu item is selected.</p>
+
+<p>This shortcut key is displayed as a tip in the menu item, below the menu item name
+(except for items in the Icon Menu, which are displayed only if the user holds the MENU
+key).</p>
+
+<p class="note"><strong>Note:</strong> Shortcut keys for menu items only work on devices with a
+hardware keyboard. Shortcuts cannot be added to items in a Context Menu.</p>
+
+
+<h3 id="intents">Intents for menu items</h3>
+
+<p>Sometimes you'll want a menu item to launch an Activity using an Intent (whether it's an
+Actvitity in your application or another application). When you know the Intent you want to use and
+have a specific menu item that should initiate the Intent, you can execute the Intent with {@link
+android.app.Activity#startActivity(Intent) startActivity()} during the appropriate on-item-selected
+callback method (such as the {@link android.app.Activity#onOptionsItemSelected(MenuItem)
+onOptionsItemSelected()} callback).</p>
+
+<p>However, if you are not certain that the user's device
+contains an application that handles the Intent, then adding a menu item that executes the
+Intent can result in a non-functioning menu item, because the Intent might not resolve to an
+Activity that accepts it. To solve this, Android lets you dynamically add menu items to your menu
+when Android finds activities on the device that handle your Intent.</p>
+
+<p>If you're not familiar with creating Intents, read the <a
+href="/guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>.</p>
+
+
+<h4>Dynamically adding Intents</h4>
+
+<p>When you don't know if the user's device has an application that handles a specific Intent,
+you can define the Intent and let Android search the device for activities that accept the Intent.
+When it finds activies that handle the Intent, it adds a menu item for
+each one to your menu and attaches the appropriate Intent to open the Activity when the user
+selects it.</p>
+
+<p>To add menu items based on available activities that accept an Intent:</p>
+<ol>
+ <li>Define an
+Intent with the category {@link android.content.Intent#CATEGORY_ALTERNATIVE} and/or
+{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE}, plus any other requirements.</li>
+ <li>Call {@link
+android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[])
+Menu.addIntentOptions()}. Android then searches for any applications that can perform the Intent
+and adds them to your menu.</li>
+</ol>
+
+<p>If there are no applications installed
+that satisfy the Intent, then no menu items are added.</p>
<p class="note"><strong>Note:</strong>
-<var>SELECTED_ALTERNATIVE</var> is used to handle the currently selected element on the
-screen. So, it should only be used when creating a Menu in <code>onCreateContextMenu()</code> or
-<code>onPrepareOptionsMenu()</code>, which is called every time the Options Menu is opened.</p>
+{@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} is used to handle the currently
+selected element on the screen. So, it should only be used when creating a Menu in {@link
+android.app.Activity#onCreateContextMenu(ContextMenu,View,ContextMenuInfo)
+onCreateContextMenu()}.</p>
-<p>Here's an example demonstrating how an application would search for
-additional services to display on its menu.</p>
+<p>For example:</p>
<pre>
+&#64;Override
public boolean onCreateOptionsMenu(Menu menu){
super.onCreateOptionsMenu(menu);
// Create an Intent that describes the requirements to fulfill, to be included
- // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
- Intent intent = new Intent(null, getIntent().getData());
+ // in our menu. The offering app must include a category value of Intent.CATEGORY_ALTERNATIVE.
+ Intent intent = new Intent(null, dataUri);
intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
-
- // Search for, and populate the menu with, acceptable offering applications.
+
+ // Search and populate the menu with acceptable offering applications.
menu.addIntentOptions(
- thisClass.INTENT_OPTIONS, // Menu group
+ R.id.intent_group, // Menu group to which new items will be added
0, // Unique item ID (none)
0, // Order for the items (none)
this.getComponentName(), // The current Activity name
@@ -495,17 +612,27 @@ public boolean onCreateOptionsMenu(Menu menu){
return true;
}</pre>
-<p>For each Activity found that provides an Intent Filter matching the Intent defined, a menu
-item will be added, using the <var>android:label</var> value of the intent filter as the text
-for the menu item.
-The <code>{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[]) addIntentOptions()}</code> method will also return the number of menu items added.</p>
-<p>Also be aware that, when <code>addIntentOptions()</code> is called, it will override any and all
-menu items in the menu group specified in the first argument.</p>
-
-<p>If you wish to offer the services of your Activity to other application menus, then you
-only need to define an intent filter as usual. Just be sure to include the <var>ALTERNATIVE</var> and/or
-<var>SELECTED_ALTERNATIVE</var> values in the <var>name</var> attribute of
-a <code>&lt;category></code> element in the intent filter. For example:</p>
+<p>For each Activity found that provides an Intent filter matching the Intent defined, a menu
+item is added, using the value in the Intent filter's <code>android:label</code> as the
+menu item title and the application icon as the menu item icon. The
+{@link android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[])
+addIntentOptions()} method returns the number of menu items added.</p>
+
+<p class="note"><strong>Note:</strong> When you call {@link
+android.view.Menu#addIntentOptions(int,int,int,ComponentName,Intent[],Intent,int,MenuItem[])
+addIntentOptions()}, it overrides any and all menu items by the menu group specified in the first
+argument.</p>
+
+
+<h4>Allowing your Activity to be added to menus</h4>
+
+<p>You can also offer the services of your Activity to other applications, so your
+application can be included in the menu of others (reverse the roles described above).</p>
+
+<p>To be included in other application menus, you need to define an Intent
+filter as usual, but be sure to include the {@link android.content.Intent#CATEGORY_ALTERNATIVE}
+and/or {@link android.content.Intent#CATEGORY_SELECTED_ALTERNATIVE} values for the Intent filter
+category. For example:</p>
<pre>
&lt;intent-filter label="Resize Image">
...
@@ -514,9 +641,10 @@ a <code>&lt;category></code> element in the intent filter. For example:</p>
...
&lt;/intent-filter>
</pre>
-<p>read more about writing intent filters in the
+
+<p>Read more about writing Intent filters in the
<a href="/guide/topics/intents/intents-filters.html">Intents and Intent Filters</a> document.</p>
<p>For a sample application using this technique, see the
-<a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a>
-sample code.</p>
+<a href="{@docRoot}resources/samples/NotePad/src/com/example/android/notepad/NoteEditor.html">Note
+Pad</a> sample code.</p>
diff --git a/docs/html/images/developing/avd-dialog.png b/docs/html/images/developing/avd-dialog.png
new file mode 100755
index 0000000..693aa42
--- /dev/null
+++ b/docs/html/images/developing/avd-dialog.png
Binary files differ
diff --git a/docs/html/images/licensing_add_library.png b/docs/html/images/licensing_add_library.png
new file mode 100644
index 0000000..90b4435
--- /dev/null
+++ b/docs/html/images/licensing_add_library.png
Binary files differ
diff --git a/docs/html/images/licensing_arch.png b/docs/html/images/licensing_arch.png
new file mode 100644
index 0000000..ba7484a
--- /dev/null
+++ b/docs/html/images/licensing_arch.png
Binary files differ
diff --git a/docs/html/images/licensing_device_signin.png b/docs/html/images/licensing_device_signin.png
new file mode 100644
index 0000000..a4f5f88
--- /dev/null
+++ b/docs/html/images/licensing_device_signin.png
Binary files differ
diff --git a/docs/html/images/licensing_flow.png b/docs/html/images/licensing_flow.png
new file mode 100644
index 0000000..b33119e
--- /dev/null
+++ b/docs/html/images/licensing_flow.png
Binary files differ
diff --git a/docs/html/images/licensing_gapis_8.png b/docs/html/images/licensing_gapis_8.png
new file mode 100644
index 0000000..43ad262
--- /dev/null
+++ b/docs/html/images/licensing_gapis_8.png
Binary files differ
diff --git a/docs/html/images/licensing_package.png b/docs/html/images/licensing_package.png
new file mode 100644
index 0000000..5da5632
--- /dev/null
+++ b/docs/html/images/licensing_package.png
Binary files differ
diff --git a/docs/html/images/licensing_public_key.png b/docs/html/images/licensing_public_key.png
new file mode 100644
index 0000000..1630209
--- /dev/null
+++ b/docs/html/images/licensing_public_key.png
Binary files differ
diff --git a/docs/html/images/licensing_test_response.png b/docs/html/images/licensing_test_response.png
new file mode 100644
index 0000000..ead2152
--- /dev/null
+++ b/docs/html/images/licensing_test_response.png
Binary files differ
diff --git a/docs/html/images/resources/clip.png b/docs/html/images/resources/clip.png
new file mode 100644
index 0000000..9196b3f
--- /dev/null
+++ b/docs/html/images/resources/clip.png
Binary files differ
diff --git a/docs/html/images/resources/layers.png b/docs/html/images/resources/layers.png
new file mode 100644
index 0000000..f7e6929
--- /dev/null
+++ b/docs/html/images/resources/layers.png
Binary files differ
diff --git a/docs/html/index.jd b/docs/html/index.jd
index 01940e8..f37a122 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -163,12 +163,11 @@ href="{@docRoot}resources/dashboard/platform-versions.html">Learn more &raquo;</
'img':"devphone-large.png",
'title':"Android Dev Phones",
'desc': "<p>Run and debug your Android applications directly on one of these "
- + "device. Modify and rebuild the Android operating system, and flash it onto "
- + "the phone. The Android Dev Phones are carrier independent, and available for "
- + "purchase by any developer registered with <a "
- + "href='http://market.android.com/publish'>Android Market</a>.</p><p><a "
- + "href='/guide/developing/device.html#dev-phone-1'>Learn more about the "
- + "Android Dev Phones &raquo;</a></p>"
+ + "devices. Modify and rebuild the Android operating system, and flash it onto "
+ + "the phone. The Android Dev Phones are carrier-independent, and available for "
+ + "purchase by developers through their Android Market publisher accounts.</p><p> "
+ + "<a href='http://market.android.com/publish'>Visit Android Market "
+ + "to learn more &raquo;</a></p>"
},
'mapskey': {
diff --git a/docs/html/license.jd b/docs/html/license.jd
index 88932b6..83cd470 100644
--- a/docs/html/license.jd
+++ b/docs/html/license.jd
@@ -74,7 +74,7 @@ slide decks that are not covered.</li>
<li>The use of sample source code provided in the SDK or shown in this
documentation is subject to the conditions detailed in the <a
-href="{@docRoot}sdk/terms.html">SDK Terms and Conditions</a>.</li>
+href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 license</a>.</li>
</ul>
</h3>
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 5e75105..ec47796 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -43,29 +43,73 @@ the development of your application features for the devices currently in
the hands of users. For information about how to target your application to devices based on
platform version, see <a href="{@docRoot}guide/appendix/api-levels.html">API Levels</a>.</p>
-<p class="note"><strong>Note:</strong> This data is based on the number
-of Android devices that have accessed Android Market within a 14-day period
-ending on the data collection date noted below.</p>
+
+<h3 id="Current">Current Distribution</h3>
+
+<p>The following pie chart and table is based on the number of Android devices that have accessed
+Android Market within a 14-day period ending on the data collection date noted below.</p>
<div class="dashboard-panel">
-<img alt="" width="460" height="250"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.1,24.6,25.0,0.1,0.3,50.0&chl=
-Android%201.1|Android%201.5|Android%201.6|Android%202.0|Android%202.0.1|Android%202.1&chco=c4df9b,
+<img alt="" height="250" width="460"
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:15.3,20.3,0.2,59.7,4.5&chl=
+Android%201.5|Android%201.6|Other*|Android%202.1|Android%202.2&chco=c4df9b,
6fad0c" />
<table>
<tr>
- <th>Android Platform</th>
- <th>Percent of Devices</th>
+ <th>Platform</th>
+ <th>API Level</th>
+ <th>Distribution</th>
</tr>
-<tr><td>Android 1.1</td><td>0.1%</td></tr>
-<tr><td>Android 1.5</td><td>24.6%</td></tr>
-<tr><td>Android 1.6</td><td>25.0%</td></tr>
-<tr><td>Android 2.0</td><td>0.1%</td></tr>
-<tr><td>Android 2.0.1</td><td>0.3%</td></tr>
-<tr><td>Android 2.1</td><td>50.0%</td></tr>
+<tr><td>Android 1.5</td><td>3</td><td>15.3%</td></tr>
+<tr><td>Android 1.6</td><td>4</td><td>20.3%</td></tr>
+<tr><td>Android 2.1</td><td>7</td><td>59.7%</td></tr>
+<tr><td>Android 2.2</td><td>8</td><td>4.5%</td></tr>
</table>
-<p><em>Data collected during two weeks ending on June 16, 2010</em></p>
-</div>
+
+<p><em>Data collected during two weeks ending on August 2, 2010</em></p>
+<p style="font-size:.9em">* <em>Other: 0.2% of devices running obsolete versions</em></p>
+
+</div><!-- end dashboard-panel -->
+
+
+<h3 id="Historical">Historical Distribution</h3>
+
+<p>The following stacked line graph provides a history of the relative number of
+active Android devices running different versions of the Android platform. It also provides a
+valuable perspective of how many devices your application is compatible with, based on the
+platform version.</p>
+
+<p>Notice that the platform versions are stacked on top of each other with the oldest active
+version at the top. This format indicates the total percent of active devices that are compatible
+with a given version of Android. For example, if you develop your application for
+the version that is at the very top of the chart, then your application is
+compatible with 100% of active devices (and all future versions), because all Android APIs are
+forward compatible. Or, if you develop your application for a version lower on the chart,
+then it is currently compatible with the percentage of devices indicated on the y-axis, where the
+line for that version meets the y-axis on the right.</p>
+
+<p>Each dataset in the timeline is based on the number of Android devices that accessed
+Android Market within a 14-day period ending on the date indicated on the x-axis.</p>
+
+<div class="dashboard-panel">
+
+<img alt="" height="250" width="660" style="padding:5px;background:#fff"
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,y,r&chxr=0,0,12|1,0,100|2,0,100&
+chxl=0%3A%7C2010/02/01%7C02/15%7C03/01%7C03/15%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/
+01%7C07/15%7C2010/08/01%7C1%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C2%3A%7C0%25%7C25%25%7C50%25
+%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.0,99.2,99.4,99.5,99.6,99.6,
+99.6,99.7,100.6,101.1,99.9,100.0,100.0|63.4,62.5,61.6,60.6,61.5,61.7,62.3,63.5,73.0,76.4,78.6,81.1,
+84.5|22.6,23.2,24.3,25.4,29.4,30.2,32.7,35.3,46.2,51.3,55.1,59.0,64.1|0.0,0.0,0.0,0.0,4.0,28.3,32.0,
+34.9,45.9,51.0,54.9,58.8,64.0|0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.8,1.2,1.8,3.3,4.3&chm=tAndroid%201.5
+,7caa36,0,0,15,,t::-5|b,c3df9b,0,1,0|tAndroid%201.6,638d23,1,0,15,,t::-5|b,b0db6e,1,2,0|tAndroid%202
+.0.1,496c13,2,0,15,,t::-5|b,9ddb3d,2,3,0|tAndroid%202.1,2f4708,3,5,15,,t::-5|b,89cf19,3,4,0|B,6fad0c
+,4,5,0&chg=7,25&chdl=Android%201.5|Android%201.6|Android%202.0.1|Android%202.1|Android%202.2&chco=
+add274,9ad145,84c323,6ba213,507d08" />
+
+<p><em>Last historical dataset collected during two weeks ending on August 2, 2010</em></p>
+
+
+</div><!-- end dashboard-panel -->
diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd
index f8130ea..90f3f1a 100644
--- a/docs/html/resources/dashboard/screens.jd
+++ b/docs/html/resources/dashboard/screens.jd
@@ -49,8 +49,8 @@ ending on the data collection date noted below.</p>
<div class="dashboard-panel">
<img alt="" width="460" height="250"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.1,57.8,41.0&chl=Small%20/%20ldpi|
-Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:2.3,0.4,45.9,51.2&chl=Small%20/%
+20ldpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" />
<table>
<tr>
@@ -60,14 +60,14 @@ Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" />
<th scope="col">High Density</th>
</tr>
<tr><th scope="row">Small</th>
-<td class='cent hi'>1.1%</td>
+<td class='cent hi'>2.3%</td>
<td></td>
<td></td>
</tr>
<tr><th scope="row">Normal</th>
-<td></td>
-<td class='cent hi'>57.8%</td>
-<td class='cent hi'>41.0%</td>
+<td class='cent '>0.4%</td>
+<td class='cent hi'>45.9%</td>
+<td class='cent hi'>51.2%</td>
</tr>
<tr><th scope="row">Large</th>
<td></td>
@@ -76,6 +76,6 @@ Normal%20/%20mdpi|Normal%20/%20hdpi&chco=c4df9b,6fad0c" />
</tr>
</table>
-<p><em>Data collected during two weeks ending on June 16, 2010</em></p>
+<p><em>Data collected during two weeks ending on August 2, 2010</em></p>
</div>
diff --git a/docs/html/resources/faq/commontasks.jd b/docs/html/resources/faq/commontasks.jd
index 2f09b00..9c32e9c 100644
--- a/docs/html/resources/faq/commontasks.jd
+++ b/docs/html/resources/faq/commontasks.jd
@@ -158,7 +158,7 @@ It is not necessary to put external JARs in the assets folder.
<ul>
<li>Create an {@link android.app.Dialog app.Dialog} class </li>
<li>Create an {@link android.app.AlertDialog app.AlertDialog} class </li>
- <li>Set the {@link android.R.style#Theme_Dialog} <em>theme</em> attribute to <code>&#064;android:style/Theme.Dialog</code>
+ <li>Set the {@link android.R.style#Theme_Dialog} <em>theme</em> attribute to <code>&#64;android:style/Theme.Dialog</code>
in your AndroidManifest.xml file. For example:
<pre>&lt;activity class=&quot;AddRssItem&quot; android:label=&quot;Add an item&quot; android:theme=&quot;&#064;android:style/Theme.Dialog&quot;/&gt;</pre></li>
</ul>
diff --git a/docs/html/resources/tutorials/notepad/codelab/NotepadCodeLab.zip b/docs/html/resources/tutorials/notepad/codelab/NotepadCodeLab.zip
index 502a326..cd30f29 100644
--- a/docs/html/resources/tutorials/notepad/codelab/NotepadCodeLab.zip
+++ b/docs/html/resources/tutorials/notepad/codelab/NotepadCodeLab.zip
Binary files differ
diff --git a/docs/html/resources/tutorials/notepad/notepad-ex2.jd b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
index a945a62..289b5fe 100644
--- a/docs/html/resources/tutorials/notepad/notepad-ex2.jd
+++ b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
@@ -393,7 +393,11 @@ case ACTIVITY_EDIT:
<pre>setContentView(R.layout.note_edit);</pre>
</li>
<li>
- Find the edit and button components we need:
+ Change the Activity title to the "Edit Note" string:
+ <pre>setTitle(R.string.edit_note);</pre>
+ </li>
+ <li>
+ Find the {@link android.widget.EditText} and {@link android.widget.Button} components we need:
<p>These are found by the
IDs associated to them in the R class, and need to be cast to the right
type of <code>View</code> (<code>EditText</code> for the two text views,
diff --git a/docs/html/sdk/android-1.5.jd b/docs/html/sdk/android-1.5.jd
index 1d6e0ad..0c16b60 100644
--- a/docs/html/sdk/android-1.5.jd
+++ b/docs/html/sdk/android-1.5.jd
@@ -139,7 +139,7 @@ function toggleDiv(link) {
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android 1.5, Revision 3</a> <em>(July 2009)</em></a>
<div class="toggleme">
<dl>
diff --git a/docs/html/sdk/android-1.6.jd b/docs/html/sdk/android-1.6.jd
index c2651b6..c4e08ff 100644
--- a/docs/html/sdk/android-1.6.jd
+++ b/docs/html/sdk/android-1.6.jd
@@ -138,7 +138,7 @@ function toggleDiv(link) {
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android 1.6, Revision 2</a> <em>(December 2009)</em></a>
<div class="toggleme">
<dl>
diff --git a/docs/html/sdk/android-2.1.jd b/docs/html/sdk/android-2.1.jd
index 7490bae..cd48a72 100644
--- a/docs/html/sdk/android-2.1.jd
+++ b/docs/html/sdk/android-2.1.jd
@@ -139,7 +139,7 @@ function toggleDiv(link) {
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android 2.1, Revision 1</a> <em>(January 2010)</em></a>
<div class="toggleme">
<dl>
diff --git a/docs/html/sdk/android-2.2.jd b/docs/html/sdk/android-2.2.jd
index f82edf9..baae92e 100644
--- a/docs/html/sdk/android-2.2.jd
+++ b/docs/html/sdk/android-2.2.jd
@@ -2,7 +2,6 @@ page.title=Android 2.2 Platform
sdk.platform.version=2.2
sdk.platform.apiLevel=8
sdk.platform.majorMinor=minor
-sdk.platform.deployableDate=May 2010
@jd:body
@@ -118,6 +117,30 @@ function toggleDiv(link) {
<div class="toggleable opened">
<a href="#" onclick="return toggleDiv(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ Android {@sdkPlatformVersion}, Revision 2</a> <em>(July 2010)</em></a>
+ <div class="toggleme">
+<dl>
+<dt>Dependencies:</dt>
+<dd>
+<p>Requires SDK Tools r6 or higher.</p>
+</dd>
+
+<dt>System Image:</dt>
+<dd>
+<ul>
+<li>Adds default Search Widget.</li>
+<li>Includes proper provisioning for the platform's Backup Manager. For more information about how to use the Backup Manager, see <a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a>.</li>
+<li>Updates the Android 2.2 system image to FRF91.</li>
+</ul>
+</dd>
+
+</dl>
+ </div>
+</div>
+
+<div class="toggleable closed">
+ <a href="#" onclick="return toggleDiv(this)">
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android {@sdkPlatformVersion}, Revision 1</a> <em>(May 2010)</em></a>
<div class="toggleme">
<dl>
@@ -135,7 +158,6 @@ function toggleDiv(link) {
</div>
</div>
-
<h2 id="api-level">API Level</h2>
<p>The Android {@sdkPlatformVersion} platform delivers an updated version of
diff --git a/docs/html/sdk/download.jd b/docs/html/sdk/download.jd
index 029de21..81b4ff6 100644
--- a/docs/html/sdk/download.jd
+++ b/docs/html/sdk/download.jd
@@ -1,80 +1,4 @@
-page.title=Download the Android SDK
-hide_license_footer=true
+sdk.redirect=true
@jd:body
-<script type="text/javascript">
- function verify() {
- document.getElementById('download-button').disabled = !document.getElementById('checkbox').checked;
- }
- function submit() {
- var location = window.location.href;
- if (location.indexOf('?v=') != -1) {
- var filename = location.substring(location.indexOf('=')+1,location.length);
- if (document.getElementById('checkbox').checked) {
- document.location = "http://dl.google.com/android/" + filename;
- }
- document.getElementById('click-download').setAttribute("href", "http://dl.google.com/android/" + filename);
- $("#terms-form").hide(500);
- $("#next-steps").show(500);
- document.getElementById('checkbox').disabled=true;
- document.getElementById('download-button').disabled=true;
- } else {
- alert("You have not selected an SDK version. Please return to the Download page");
- }
- }
-</script>
-
-<div id="terms-form">
- <p>Please carefully review the Android SDK License Agreement before downloading the SDK.
-The License Agreement constitutes a contract between you and Google with respect to your use of the SDK.</p>
-
- <iframe id="terms" style="border:1px solid #888;margin:0 0 1em;height:400px;width:95%;" src="terms_body.html">
- </iframe>
-
- <p>
- <input type="checkbox" id="checkbox" onclick="verify()" />
- <label for="checkbox">I agree to the terms of the Android SDK License Agreement.</label>
- </p>
- <p>
- <input type="submit" value="Download" id="download-button" disabled="disabled" onclick="submit()" />
- </p>
- <p>
- <script language="javascript">
- var loc = window.location.href;
- if (loc.indexOf('?v=') != -1) {
- var filename = loc.substring(loc.indexOf('=')+1,loc.length);
- document.write("File: " + filename);
- }
- </script>
- </p>
-</div><!-- end terms-form -->
-
-<noscript>
- <p><strong>Please enable Javascript in your browser in order to agree to the terms and download the SDK.</strong></p>
-</noscript>
-
-<div class="special" id="next-steps" style="display:none">
- <h2>Thank you for downloading the Android SDK!</h2>
- <p>Your download should be underway. If not, <a id="click-download">click here to start the download</a>.</p>
- <p>To set up your Android development environment, please read the guide to
- <a href="installing.html">Installing the Android SDK</a> and ensure that your development
- machine meets the system requirements linked on that page.</p>
-</div>
-
-<script type="text/javascript">
- var loc = window.location.href;
- var filename = loc.substring(loc.indexOf('=')+1,loc.length);
- version = filename.substring(filename.indexOf('.')-1,filename.lastIndexOf('.'));
- $(".addVersionPath").each(function(i) {
- var oldHref = $(this).attr("href");
- $(this).attr({href: "/sdk/" + version + "/" + oldHref});
- });
-</script>
-
-
-
-
-
-
-
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index f5558ab..bd7eeed 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -48,6 +48,9 @@ href="#installing">Installing the ADT Plugin</a>, below. </p>
how to update ADT to the latest version or how to uninstall it, if necessary.
</p>
+<p class="caution"><strong>Caution:</strong> There are known issues with the ADT plugin running with
+Eclipse 3.6. Please stay on 3.5 until further notice.</p>
+
<h2 id="notes">Revisions</h2>
<p>The sections below provide notes about successive releases of
@@ -295,7 +298,7 @@ location:
<p>Additionally, before you can configure or use ADT, you must install the
Android SDK starter package, as described in <a
-href="installing.html#Installing">Downloading the SDK Starter Pacskage</a>.
+href="installing.html#Installing">Downloading the SDK Starter Package</a>.
Specifically, you need to install a compatible version of the Android SDK Tools
and at least one development platform. To simplify ADT setup, we recommend
installing the Android SDK prior to installing ADT. </p>
diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd
index 69cc73d..9e88d94 100644
--- a/docs/html/sdk/ndk/index.jd
+++ b/docs/html/sdk/ndk/index.jd
@@ -1,16 +1,16 @@
ndk=true
-ndk.win_download=android-ndk-r4-windows.zip
-ndk.win_bytes=45778965
-ndk.win_checksum=1eded98a7f5cd5e71f8ac74565f73f11
+ndk.win_download=android-ndk-r4b-windows.zip
+ndk.win_bytes=45792835
+ndk.win_checksum=e397145e155a639be53ee4b6db8ad511
-ndk.mac_download=android-ndk-r4-darwin-x86.zip
-ndk.mac_bytes=50572163
-ndk.mac_checksum=b7d5f149fecf951c05a79b045f00419f
+ndk.mac_download=android-ndk-r4b-darwin-x86.zip
+ndk.mac_bytes=50586041
+ndk.mac_checksum=41dbd54335fb828ee408eab17103a1b0
-ndk.linux_download=android-ndk-r4-linux-x86.zip
-ndk.linux_bytes=49450682
-ndk.linux_checksum=0892b0637d45d145e045cc68e163dee3
+ndk.linux_download=android-ndk-r4b-linux-x86.zip
+ndk.linux_bytes=49464776
+ndk.linux_checksum=2deabcb125c219b34140975b710f00ec
page.title=Android NDK
@jd:body
@@ -62,8 +62,15 @@ padding: .25em 1em;
<div class="toggleable open">
<a href="#" onclick="return toggleDiv(this)">
<img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
-Android NDK, Revision 4</a> <em>(May 2010)</em>
+Android NDK, Revision 4b</a> <em>(June 2010)</em>
<div class="toggleme">
+<dl>
+<dt>NDK r4b notes:</dt>
+<dd><p>Includes fixes for several issues in the NDK build and debugging scripts
+&mdash; if you are using NDK r4, we recommend downloading the NDK r4b build. For
+detailed information the changes in this release, read the CHANGES.TXT document
+included in the downloaded NDK package.</p></dd>
+</dl>
<dl>
<dt>General notes:</dt>
@@ -114,7 +121,7 @@ code.</li>
<div class="toggleable closed">
<a href="#" onclick="return toggleDiv(this)">
- <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" />
+ <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" />
Android NDK, Revision 3</a> <em>(March 2010)</em>
<div class="toggleme">
diff --git a/docs/html/sdk/older_releases.jd b/docs/html/sdk/older_releases.jd
index c3ba495..77f7e43 100644
--- a/docs/html/sdk/older_releases.jd
+++ b/docs/html/sdk/older_releases.jd
@@ -47,7 +47,7 @@ Notes</a></em></p>
<td>Windows</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.6_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.6_r1.zip">android-sdk-
windows-1 .6_r1.zip</a>
</td>
<td>260529085 bytes</td>
@@ -57,7 +57,7 @@ windows-1 .6_r1.zip</a>
<td>Mac OS X (intel)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.6_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.6_r1.zip">android-sdk-
mac_x86-1 .6_r1.zip</a>
</td>
<td>247412515 bytes</td>
@@ -67,7 +67,7 @@ mac_x86-1 .6_r1.zip</a>
<td>Linux (i386)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.6_r1.tgz">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.6_r1.tgz">android-
sdk- linux_x86-1.6_r1.tgz</a>
</td>
<td>238224860 bytes</td>
@@ -92,7 +92,7 @@ Notes</a></em></p>
<td>Windows</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r3.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r3.zip">android-sdk-
windows-1 .5_r3.zip</a>
</td>
<td>191477853 bytes</td>
@@ -102,7 +102,7 @@ windows-1 .5_r3.zip</a>
<td>Mac OS X (intel)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r3.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r3.zip">android-sdk-
mac_x86-1 .5_r3.zip</a>
</td>
<td>183024673 bytes</td>
@@ -112,7 +112,7 @@ mac_x86-1 .5_r3.zip</a>
<td>Linux (i386)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r3.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r3.zip">android-
sdk- linux_x86-1.5_r3.zip</a>
</td>
<td>178117561 bytes</td>
@@ -137,7 +137,7 @@ Notes</a></em></p>
<td>Windows</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.1_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.1_r1.zip">android-sdk-
windows-1
.1_r1.zip</a>
</td>
@@ -148,7 +148,7 @@ windows-1
<td>Mac OS X (intel)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.1_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.1_r1.zip">android-sdk-
mac_x86-1
.1_r1.zip</a>
</td>
@@ -159,7 +159,7 @@ mac_x86-1
<td>Linux (i386)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.1_r1.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.1_r1.zip">android-
sdk-
linux_x86-1.1_r1.zip</a>
</td>
@@ -185,7 +185,7 @@ Notes</a></em></p>
<td>Windows</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.0_r2.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.0_r2.zip">android-sdk-
windows-1
.0_r2.zip</a>
</td>
@@ -196,7 +196,7 @@ windows-1
<td>Mac OS X (intel)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.0_r2.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.0_r2.zip">android-sdk-
mac_x86-1
.0_r2.zip</a>
</td>
@@ -207,7 +207,7 @@ mac_x86-1
<td>Linux (i386)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.0_r2.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.0_r2.zip">android-
sdk-
linux_x86-1.0_r2.zip</a>
</td>
@@ -241,7 +241,7 @@ Notes</a></em></p>
<td>Windows</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r2.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r2.zip">android-sdk-
windows-1 .5_r2.zip</a>
</td>
<td>178346828 bytes</td>
@@ -251,7 +251,7 @@ windows-1 .5_r2.zip</a>
<td>Mac OS X (intel)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r2.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r2.zip">android-sdk-
mac_x86-1 .5_r2.zip</a>
</td>
<td>169945128 bytes</td>
@@ -261,7 +261,7 @@ mac_x86-1 .5_r2.zip</a>
<td>Linux (i386)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r2.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r2.zip">android-
sdk- linux_x86-1.5_r2.zip</a>
</td>
<td>165035130 bytes</td>
@@ -286,7 +286,7 @@ Notes</a></em></p>
<td>Windows</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.5_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.5_r1.zip">android-sdk-
windows-1 .5_r1.zip</a>
</td>
<td>176263368 bytes</td>
@@ -296,7 +296,7 @@ windows-1 .5_r1.zip</a>
<td>Mac OS X (intel)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.5_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.5_r1.zip">android-sdk-
mac_x86-1 .5_r1.zip</a>
</td>
<td>167848675 bytes</td>
@@ -306,7 +306,7 @@ mac_x86-1 .5_r1.zip</a>
<td>Linux (i386)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.5_r1.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.5_r1.zip">android-
sdk- linux_x86-1.5_r1.zip</a>
</td>
<td>162938845 bytes</td>
@@ -331,7 +331,7 @@ Notes</a></em></p>
<td>Windows</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-windows-1.0_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-windows-1.0_r1.zip">android-sdk-
windows-1 .0_r1.zip</a>
</td>
<td>89.7 MB bytes</td>
@@ -341,7 +341,7 @@ windows-1 .0_r1.zip</a>
<td>Mac OS X (intel)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-mac_x86-1.0_r1.zip">android-sdk-
+href="http://dl.google.com/android/archives/android-sdk-mac_x86-1.0_r1.zip">android-sdk-
mac_x86-1 .0_r1.zip</a>
</td>
<td>87.5 MB bytes</td>
@@ -351,7 +351,7 @@ mac_x86-1 .0_r1.zip</a>
<td>Linux (i386)</td>
<td>
<a
-href="/sdk/download.html?v=archives/android-sdk-linux_x86-1.0_r1.zip">android-
+href="http://dl.google.com/android/archives/android-sdk-linux_x86-1.0_r1.zip">android-
sdk- linux_x86-1.0_r1.zip</a>
</td>
<td>87.8 MB bytes</td>
diff --git a/docs/html/sdk/requirements.jd b/docs/html/sdk/requirements.jd
index cb9cdf3..d710b8e 100644
--- a/docs/html/sdk/requirements.jd
+++ b/docs/html/sdk/requirements.jd
@@ -23,6 +23,9 @@ installation notes</a>.</li>
<h4 style="margin-top:.25em"><em>Eclipse IDE</em></h4>
<ul>
<li>Eclipse 3.4 (Ganymede) or 3.5 (Galileo)
+ <p class="caution"><strong>Caution:</strong> There are known issues with the ADT plugin
+running with Eclipse 3.6. Please stay on 3.5 until further notice.</p>
+ </li>
<li>Eclipse <a href="http://www.eclipse.org/jdt">JDT</a> plugin (included
in most Eclipse IDE packages) </li>
<li>If you need to install or update Eclipse, you can download it from <a
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 7438707..404e938 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -75,7 +75,8 @@
</li>
</ul>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r6</a> <span class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>sdk/tools-notes.html">SDK Tools, r6</a>
+ </li>
<li><a href="<?cs var:toroot ?>sdk/win-usb.html">USB Driver for
Windows, r3</a>
</li>
@@ -101,7 +102,6 @@
<span style="display:none" class="ja"></span>
<span style="display:none" class="zh-CN"></span>
<span style="display:none" class="zh-TW"></span></a>
- <span class="new">new!</span>
</li>
</ul>
</li>
@@ -116,7 +116,7 @@
<span style="display:none" class="zh-TW"></span>
</h2>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r4</a>
+ <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r4b</a>
<span class="new">new!</span></li>
</ul>
</li>
@@ -133,7 +133,6 @@
</h2>
<ul>
<li><a href="<?cs var:toroot ?>sdk/requirements.html">SDK System Requirements</a></li>
- <li><a href="<?cs var:toroot ?>sdk/terms.html">SDK Terms and Conditions</a></li>
<!-- <li><a href="<?cs var:toroot ?>sdk/RELEASENOTES.html">SDK Release
Notes</a></li> -->
<li><a href="<?cs var:toroot ?>sdk/older_releases.html">SDK
diff --git a/docs/html/sdk/terms_body.html b/docs/html/sdk/terms_body.html
deleted file mode 100644
index 03e0906..0000000
--- a/docs/html/sdk/terms_body.html
+++ /dev/null
@@ -1,204 +0,0 @@
-
-<p>This is the Android Software Development Kit License Agreement.</p>
-
-<h2>
- 1. Introduction
-</h2>
-<p>
- 1.1 The Android Software Development Kit (referred to in this License Agreement as the "SDK" and specifically including the Android system files, packaged APIs, and Google APIs add-ons) is licensed to you subject to the terms of this License Agreement. This License Agreement forms a legally binding contract between you and Google in relation to your use of the SDK.
-
-</p>
-<p>
- 1.2 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States.
-</p>
-<h2>
- 2. Accepting this License Agreement
-</h2>
-<p>
- 2.1 In order to use the SDK, you must first agree to this License Agreement. You may not use the SDK if you do not accept this License Agreement.
-</p>
-<p>
- 2.2 You can accept this License Agreement by:
-</p>
-<p>
- (A) clicking to accept or agree to this License Agreement, where this option is made available to you; or
-</p>
-<p>
- (B) by actually using the SDK. In this case, you agree that use of the SDK constitutes acceptance of the Licensing Agreement from that point onwards.
-</p>
-<p>
- 2.3 You may not use the SDK and may not accept the Licensing Agreement if you are a person barred from receiving the SDK under the laws of the United States or other countries including the country in which you are resident or from which you use the SDK.
-</p>
-<p>
- 2.4 If you are agreeing to be bound by this License Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to this License Agreement. If you do not have the requisite authority, you may not accept the Licensing Agreement or use the SDK on behalf of your employer or other entity.
-</p>
-<h2>
- 3. SDK License from Google
-</h2>
-<p>
- 3.1 Subject to the terms of this License Agreement, Google grants you a limited, worldwide, royalty-free, non- assignable and non-exclusive license to use the SDK solely to develop applications to run on the Android platform.
-</p>
-<p>
- 3.2 You agree that Google or third parties own all legal right, title and interest in and to the SDK, including any Intellectual Property Rights that subsist in the SDK. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Google reserves all rights not expressly granted to you.
-
-</p>
-<p>
- 3.3 Except to the extent required by applicable third party licenses, you may not copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the SDK or any part of the SDK. Except to the extent required by applicable third party licenses, you may not load any part of the SDK onto a mobile handset or any other hardware device except a personal computer, combine any part of the SDK with other software, or distribute any software or device incorporating a part of the SDK.
-</p>
-<p>
- 3.4 Use, reproduction and distribution of components of the SDK licensed under an open source software license are governed solely by the terms of that open source software license and not this License Agreement.
-</p>
-<p>
- 3.5 You agree that the form and nature of the SDK that Google provides may change without prior notice to you and that future versions of the SDK may be incompatible with applications developed on previous versions of the SDK. You agree that Google may stop (permanently or temporarily) providing the SDK (or any features within the SDK) to you or to users generally at Google's sole discretion, without prior notice to you.
-</p>
-<p>
- 3.6 Nothing in this License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features.
-</p>
-<p>
- 3.7 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the SDK.
-</p>
-<h2>
- 4. Use of the SDK by You
-</h2>
-<p>
- 4.1 Google agrees that it obtains no right, title or interest from you (or your licensors) under this License Agreement in or to any software applications that you develop using the SDK, including any intellectual property rights that subsist in those applications.
-</p>
-<p>
- 4.2 You agree to use the SDK and write applications only for purposes that are permitted by (a) this License Agreement and (b) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries).
-</p>
-<p>
- 4.3 You agree that if you use the SDK to develop applications for general public users, you will protect the privacy and legal rights of those users. If the users provide you with user names, passwords, or other login information or personal information, your must make the users aware that the information will be available to your application, and you must provide legally adequate privacy notice and protection for those users. If your application stores personal or sensitive information provided by users, it must do so securely. If the user provides your application with Google Account information, your application may only use that information to access the user's Google Account when, and for the limited purposes for which, the user has given you permission to do so.
-</p>
-<p>
- 4.4 You agree that you will not engage in any activity with the SDK, including the development or distribution of an application, that interferes with, disrupts, damages, or accesses in an unauthorized manner the servers, networks, or other properties or services of any third party including, but not limited to, Google or any mobile communications carrier.
-</p>
-<p>
- 4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any data, content, or resources that you create, transmit or display through the Android platform and/or applications for the Android platform, and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so.
-</p>
-<p>
- 4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under this License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach.
-</p>
-<h2>
- 5. Your Developer Credentials
-</h2>
-<p>
- 5.1 You agree that you are responsible for maintaining the confidentiality of any developer credentials that may be issued to you by Google or which you may choose yourself and that you will be solely responsible for all applications that are developed under your developer credentials.
-</p>
-<h2>
- 6. Privacy and Information
-</h2>
-<p>
- 6.1 In order to continually innovate and improve the SDK, Google may collect certain usage statistics from the software including but not limited to a unique identifier, associated IP address, version number of the software, and information on which tools and/or services in the SDK are being used and how they are being used. Before any of this information is collected, the SDK will notify you and seek your consent. If you withhold consent, the information will not be collected.
-</p>
-<p>
- 6.2 The data collected is examined in the aggregate to improve the SDK and is maintained in accordance with Google's Privacy Policy.
-</p>
-<h2>
- 7. Third Party Applications for the Android Platform
-</h2>
-<p>
- 7.1 If you use the SDK to run applications developed by a third party or that access data, content or resources provided by a third party, you agree that Google is not responsible for those applications, data, content, or resources. You understand that all data, content or resources which you may access through such third party applications are the sole responsibility of the person from which they originated and that Google is not liable for any loss or damage that you may experience as a result of the use or access of any of those third party applications, data, content, or resources.
-</p>
-<p>
- 7.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners.
-</p>
-<p>
- 7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party. In that case, this License Agreement does not affect your legal relationship with these third parties.
-</p>
-<h2>
- 8. Using Android APIs
-</h2>
-<p>
- 8.1 Google Data APIs
-</p>
-<p>
- 8.1.1 If you use any API to retrieve data from Google, you acknowledge that the data may be protected by intellectual property rights which are owned by Google or those parties that provide the data (or by other persons or companies on their behalf). Your use of any such API may be subject to additional Terms of Service. You may not modify, rent, lease, loan, sell, distribute or create derivative works based on this data (either in whole or in part) unless allowed by the relevant Terms of Service.
-</p>
-<p>
- 8.1.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree that you shall retrieve data only with the user's explicit consent and only when, and for the limited purposes for which, the user has given you permission to do so.
-
-</p>
-<h2>
- 9. Terminating this License Agreement
-</h2>
-<p>
- 9.1 This License Agreement will continue to apply until terminated by either you or Google as set out below.
-</p>
-<p>
- 9.2 If you want to terminate this License Agreement, you may do so by ceasing your use of the SDK and any relevant developer credentials.
-</p>
-<p>
- 9.3 Google may at any time, terminate this License Agreement with you if:
-</p>
-<p>
- (A) you have breached any provision of this License Agreement; or
-</p>
-<p>
- (B) Google is required to do so by law; or
-</p>
-<p>
- (C) the partner with whom Google offered certain parts of SDK (such as APIs) to you has terminated its relationship with Google or ceased to offer certain parts of the SDK to you; or
-</p>
-<p>
- (D) Google decides to no longer providing the SDK or certain parts of the SDK to users in the country in which you are resident or from which you use the service, or the provision of the SDK or certain SDK services to you by Google is, in Google's sole discretion, no longer commercially viable.
-</p>
-<p>
- 9.4 When this License Agreement comes to an end, all of the legal rights, obligations and liabilities that you and Google have benefited from, been subject to (or which have accrued over time whilst this License Agreement has been in force) or which are expressed to continue indefinitely, shall be unaffected by this cessation, and the provisions of paragraph 14.7 shall continue to apply to such rights, obligations and liabilities indefinitely.
-</p>
-<h2>
- 10. DISCLAIMER OF WARRANTIES
-</h2>
-<p>
- 10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE SDK IS AT YOUR SOLE RISK AND THAT THE SDK IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE.
-</p>
-<p>
- 10.2 YOUR USE OF THE SDK AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE SDK IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE.
-</p>
-<p>
- 10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
-</p>
-<h2>
- 11. LIMITATION OF LIABILITY
-</h2>
-<p>
- 11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING.
-</p>
-<h2>
- 12. Indemnification
-</h2>
-<p>
- 12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys fees) arising out of or accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with this License Agreement.
-</p>
-<h2>
- 13. Changes to the License Agreement
-</h2>
-<p>
- 13.1 Google may make changes to the License Agreement as it distributes new versions of the SDK. When these changes are made, Google will make a new version of the License Agreement available on the website where the SDK is made available.
-</p>
-<h2>
- 14. General Legal Terms
-</h2>
-<p>
- 14.1 This License Agreement constitute the whole legal agreement between you and Google and govern your use of the SDK (excluding any services which Google may provide to you under a separate written agreement), and completely replace any prior agreements between you and Google in relation to the SDK.
-</p>
-<p>
- 14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in this License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google.
-</p>
-<p>
- 14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of this License Agreement is invalid, then that provision will be removed from this License Agreement without affecting the rest of this License Agreement. The remaining provisions of this License Agreement will continue to be valid and enforceable.
-</p>
-<p>
- 14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to this License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of this License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to this License Agreement.
-</p>
-<p>
- 14.5 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE SDK. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE.
-</p>
-<p>
- 14.6 The rights granted in this License Agreement may not be assigned or transferred by either you or Google without the prior written approval of the other party. Neither you nor Google shall be permitted to delegate their responsibilities or obligations under this License Agreement without the prior written approval of the other party.
-</p>
-<p>
- 14.7 This License Agreement, and your relationship with Google under this License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from this License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.
-</p>
-<p>
- <em>April 10, 2009</em>
-</p>
diff --git a/docs/html/sitemap.txt b/docs/html/sitemap.txt
index b0ec7c9..d5be8f1 100644
--- a/docs/html/sitemap.txt
+++ b/docs/html/sitemap.txt
@@ -7,7 +7,6 @@ http://developer.android.com/resources/index.html
http://developer.android.com/videos/index.html
http://developer.android.com/resources/dashboard/platform-versions.html
http://developer.android.com/license.html
-http://developer.android.com/sdk/terms.html
http://developer.android.com/resources/community-groups.html
http://developer.android.com/resources/community-more.html
http://developer.android.com/resources/articles/index.html
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index fdc4c92..e275ba8 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -64,6 +64,8 @@ import android.util.AttributeSet;
* // Start the animation (looped playback by default).
* frameAnimation.start()
* </pre>
+ * <p>For more information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p>
*
* @attr ref android.R.styleable#AnimationDrawable_visible
* @attr ref android.R.styleable#AnimationDrawable_variablePadding
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 29e14d2..32111e8 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -40,7 +40,9 @@ import java.io.IOException;
* A Drawable that wraps a bitmap and can be tiled, stretched, or aligned. You can create a
* BitmapDrawable from a file path, an input stream, through XML inflation, or from
* a {@link android.graphics.Bitmap} object.
- * <p>It can be defined in an XML file with the <code>&lt;bitmap></code> element.</p>
+ * <p>It can be defined in an XML file with the <code>&lt;bitmap></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
* <p>
* Also see the {@link android.graphics.Bitmap} class, which handles the management and
* transformation of raw bitmap graphics, and should be used when drawing to a
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index c387a9b..a772871 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -32,9 +32,14 @@ import java.io.IOException;
* level value. You can control how much the child Drawable gets clipped in width
* and height based on the level, as well as a gravity to control where it is
* placed in its overall container. Most often used to implement things like
- * progress bars.
+ * progress bars, by increasing the drawable's level with {@link
+ * android.graphics.drawable.Drawable#setLevel(int) setLevel()}.
+ * <p class="note"><strong>Note:</strong> The drawable is clipped completely and not visible when
+ * the level is 0 and fully revealed when the level is 10,000.</p>
*
- * <p>It can be defined in an XML file with the <code>&lt;clip></code> element.</p>
+ * <p>It can be defined in an XML file with the <code>&lt;clip></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#ClipDrawable_clipOrientation
* @attr ref android.R.styleable#ClipDrawable_gravity
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 6a7b2d1..3125321 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -93,8 +93,8 @@ import android.util.TypedValue;
* whose overall size is modified based on the current level.
* </ul>
* <p>For information and examples of creating drawable resources (XML or bitmap files that
- * can be loaded in code), see <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources
- * and Internationalization</a>.
+ * can be loaded in code), see <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
*/
public abstract class Drawable {
private static final Rect ZERO_BOUNDS_RECT = new Rect();
@@ -709,8 +709,7 @@ public abstract class Drawable {
/**
* Create a drawable from an XML document. For more information on how to
* create resources in XML, see
- * <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources and
- * Internationalization</a>.
+ * <a href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
*/
public static Drawable createFromXml(Resources r, XmlPullParser parser)
throws XmlPullParserException, IOException {
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 63d1446..33ecbea 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -42,7 +42,9 @@ import java.io.IOException;
/**
* A Drawable with a color gradient for buttons, backgrounds, etc.
*
- * <p>It can be defined in an XML file with the <code>&lt;shape></code> element.</p>
+ * <p>It can be defined in an XML file with the <code>&lt;shape></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#GradientDrawable_visible
* @attr ref android.R.styleable#GradientDrawable_shape
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 4fa9d44..a9c983e 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -32,7 +32,9 @@ import java.io.IOException;
* This is used when a View needs a background that is smaller than
* the View's actual bounds.
*
- * <p>It can be defined in an XML file with the <code>&lt;inset></code> element.</p>
+ * <p>It can be defined in an XML file with the <code>&lt;inset></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#InsetDrawable_visible
* @attr ref android.R.styleable#InsetDrawable_drawable
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 389fd40..8047dd4 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -32,8 +32,9 @@ import java.io.IOException;
* order, so the element with the largest index will be drawn on top.
* <p>
* It can be defined in an XML file with the <code>&lt;layer-list></code> element.
- * Each Drawable in the layer is defined in a nested <code>&lt;item></code>.
- * </p>
+ * Each Drawable in the layer is defined in a nested <code>&lt;item></code>. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#LayerDrawableItem_left
* @attr ref android.R.styleable#LayerDrawableItem_top
diff --git a/graphics/java/android/graphics/drawable/LevelListDrawable.java b/graphics/java/android/graphics/drawable/LevelListDrawable.java
index ae8f224..21be983 100644
--- a/graphics/java/android/graphics/drawable/LevelListDrawable.java
+++ b/graphics/java/android/graphics/drawable/LevelListDrawable.java
@@ -47,7 +47,10 @@ import android.util.AttributeSet;
* <p>With this XML saved into the res/drawable/ folder of the project, it can be referenced as
* the drawable for an {@link android.widget.ImageView}. The default image is the first in the list.
* It can then be changed to one of the other levels with
- * {@link android.widget.ImageView#setImageLevel(int)}.</p>
+ * {@link android.widget.ImageView#setImageLevel(int)}. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
+ *
* @attr ref android.R.styleable#LevelListDrawableItem_minLevel
* @attr ref android.R.styleable#LevelListDrawableItem_maxLevel
* @attr ref android.R.styleable#LevelListDrawableItem_drawable
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 2083e05..9c47dab 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -35,7 +35,9 @@ import java.io.IOException;
* value. The start and end angles of rotation can be controlled to map any
* circular arc to the level values range.</p>
*
- * <p>It can be defined in an XML file with the <code>&lt;rotate></code> element.</p>
+ * <p>It can be defined in an XML file with the <code>&lt;rotate></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/animation-resource.html">Animation Resources</a>.</p>
*
* @attr ref android.R.styleable#RotateDrawable_visible
* @attr ref android.R.styleable#RotateDrawable_fromDegrees
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index 275e36f..b623d80 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -34,7 +34,9 @@ import java.io.IOException;
* placed in its overall container. Most often used to implement things like
* progress bars.
*
- * <p>It can be defined in an XML file with the <code>&lt;scale></code> element.</p>
+ * <p>It can be defined in an XML file with the <code>&lt;scale></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#ScaleDrawable_scaleWidth
* @attr ref android.R.styleable#ScaleDrawable_scaleHeight
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index c699a82..be1892e 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -34,6 +34,10 @@ import java.io.IOException;
* the ShapeDrawable will default to a
* {@link android.graphics.drawable.shapes.RectShape}.
*
+ * <p>It can be defined in an XML file with the <code>&lt;shape></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
+ *
* @attr ref android.R.styleable#ShapeDrawablePadding_left
* @attr ref android.R.styleable#ShapeDrawablePadding_top
* @attr ref android.R.styleable#ShapeDrawablePadding_right
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index b94df84..239be40 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -31,7 +31,9 @@ import android.util.StateSet;
* ID value.
* <p/>
* <p>It can be defined in an XML file with the <code>&lt;selector></code> element.
- * Each state Drawable is defined in a nested <code>&lt;item></code> element.</p>
+ * Each state Drawable is defined in a nested <code>&lt;item></code> element. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
*
* @attr ref android.R.styleable#StateListDrawable_visible
* @attr ref android.R.styleable#StateListDrawable_variablePadding
diff --git a/graphics/java/android/graphics/drawable/TransitionDrawable.java b/graphics/java/android/graphics/drawable/TransitionDrawable.java
index 97b45d8..4470356 100644
--- a/graphics/java/android/graphics/drawable/TransitionDrawable.java
+++ b/graphics/java/android/graphics/drawable/TransitionDrawable.java
@@ -26,8 +26,10 @@ import android.os.SystemClock;
* display just the first layer, call {@link #resetTransition()}.
* <p>
* It can be defined in an XML file with the <code>&lt;transition></code> element.
- * Each Drawable in the transition is defined in a nested <code>&lt;item></code>.
- * </p>
+ * Each Drawable in the transition is defined in a nested <code>&lt;item></code>. For more
+ * information, see the guide to <a
+ * href="{@docRoot}guide/topics/resources/drawable-resource.html">Drawable Resources</a>.</p>
+ *
* @attr ref android.R.styleable#LayerDrawableItem_left
* @attr ref android.R.styleable#LayerDrawableItem_top
* @attr ref android.R.styleable#LayerDrawableItem_right
@@ -212,7 +214,7 @@ public class TransitionDrawable extends LayerDrawable implements Drawable.Callba
* Enables or disables the cross fade of the drawables. When cross fade
* is disabled, the first drawable is always drawn opaque. With cross
* fade enabled, the first drawable is drawn with the opposite alpha of
- * the second drawable.
+ * the second drawable. Cross fade is disabled by default.
*
* @param enabled True to enable cross fading, false otherwise.
*/
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index ee2b30c..57968e3 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2008 The Android Open Source Project
- * Copyright (C) 2008 HTC Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index 24ac82b..73bf2ee 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -1,6 +1,6 @@
/*
**
- ** Copyright 2008, HTC Inc.
+ ** Copyright 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.
diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h
index 4f17c1a..2a1298a 100644
--- a/include/media/PVMediaRecorder.h
+++ b/include/media/PVMediaRecorder.h
@@ -1,6 +1,6 @@
/*
**
- ** Copyright 2008, HTC Inc.
+ ** Copyright 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.
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 9af5871..9a09586 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -86,6 +86,10 @@ private:
bool mStarted;
+ bool mIsFirstBuffer;
+ status_t mFirstBufferResult;
+ MediaBuffer *mFirstBuffer;
+
sp<MediaPlayerBase::AudioSink> mAudioSink;
static void AudioCallback(int event, void *user, void *info);
diff --git a/libs/camera/Camera.cpp b/libs/camera/Camera.cpp
index f19c502..30ee7c4 100644
--- a/libs/camera/Camera.cpp
+++ b/libs/camera/Camera.cpp
@@ -1,7 +1,6 @@
/*
**
** Copyright (C) 2008, The Android Open Source Project
-** Copyright (C) 2008 HTC Inc.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index b3aae72..f1fa1e8 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -19,7 +19,7 @@ package android.media;
/**
* The AudioFormat class is used to access a number of audio format and
* channel configuration constants. They are for instance used
- * in __link AudioTrack} and __link AudioRecord}.
+ * in {@link AudioTrack} and {@link AudioRecord}.
*
*/
public class AudioFormat {
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 5c278d9..08fc782 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -16,6 +16,7 @@
package android.media;
+import java.util.NoSuchElementException;
import android.app.ActivityManagerNative;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -1016,7 +1017,11 @@ public class AudioService extends IAudioService.Stub {
} else {
mStartcount--;
if (mStartcount == 0) {
- mCb.unlinkToDeath(this, 0);
+ try {
+ mCb.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ Log.w(TAG, "decCount() going to 0 but not registered to binder");
+ }
}
requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
}
@@ -1025,8 +1030,14 @@ public class AudioService extends IAudioService.Stub {
public void clearCount(boolean stopSco) {
synchronized(mScoClients) {
+ if (mStartcount != 0) {
+ try {
+ mCb.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
+ }
+ }
mStartcount = 0;
- mCb.unlinkToDeath(this, 0);
if (stopSco) {
requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
}
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 2bc2a7e..1de9f9b 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -1,6 +1,6 @@
/*
**
- ** Copyright 2008, HTC Inc.
+ ** Copyright 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.
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index c507669..12de0d9 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -1,5 +1,5 @@
/*
- ** Copyright 2008, HTC Inc.
+ ** Copyright 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.
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index e07306b..805005d 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -1,6 +1,6 @@
/*
**
- ** Copyright 2008, HTC Inc.
+ ** Copyright 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.
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index bcf2463..c27cfc8 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -23,6 +23,7 @@
#include <media/stagefright/AudioPlayer.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
@@ -41,6 +42,9 @@ AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink)
mReachedEOS(false),
mFinalStatus(OK),
mStarted(false),
+ mIsFirstBuffer(false),
+ mFirstBufferResult(OK),
+ mFirstBuffer(NULL),
mAudioSink(audioSink) {
}
@@ -68,6 +72,24 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) {
}
}
+ // We allow an optional INFO_FORMAT_CHANGED at the very beginning
+ // of playback, if there is one, getFormat below will retrieve the
+ // updated format, if there isn't, we'll stash away the valid buffer
+ // of data to be used on the first audio callback.
+
+ CHECK(mFirstBuffer == NULL);
+
+ mFirstBufferResult = mSource->read(&mFirstBuffer);
+ if (mFirstBufferResult == INFO_FORMAT_CHANGED) {
+ LOGV("INFO_FORMAT_CHANGED!!!");
+
+ CHECK(mFirstBuffer == NULL);
+ mFirstBufferResult = OK;
+ mIsFirstBuffer = false;
+ } else {
+ mIsFirstBuffer = true;
+ }
+
sp<MetaData> format = mSource->getFormat();
const char *mime;
bool success = format->findCString(kKeyMIMEType, &mime);
@@ -87,7 +109,14 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) {
DEFAULT_AUDIOSINK_BUFFERCOUNT,
&AudioPlayer::AudioSinkCallback, this);
if (err != OK) {
- mSource->stop();
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+
+ if (!sourceAlreadyStarted) {
+ mSource->stop();
+ }
return err;
}
@@ -108,7 +137,14 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) {
delete mAudioTrack;
mAudioTrack = NULL;
- mSource->stop();
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+
+ if (!sourceAlreadyStarted) {
+ mSource->stop();
+ }
return err;
}
@@ -159,6 +195,12 @@ void AudioPlayer::stop() {
// Make sure to release any buffer we hold onto so that the
// source is able to stop().
+
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+
if (mInputBuffer != NULL) {
LOGV("AudioPlayer releasing input buffer.");
@@ -243,6 +285,14 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) {
Mutex::Autolock autoLock(mLock);
if (mSeeking) {
+ if (mIsFirstBuffer) {
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+ mIsFirstBuffer = false;
+ }
+
options.setSeekTo(mSeekTimeUs);
if (mInputBuffer != NULL) {
@@ -255,7 +305,17 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) {
}
if (mInputBuffer == NULL) {
- status_t err = mSource->read(&mInputBuffer, &options);
+ status_t err;
+
+ if (mIsFirstBuffer) {
+ mInputBuffer = mFirstBuffer;
+ mFirstBuffer = NULL;
+ err = mFirstBufferResult;
+
+ mIsFirstBuffer = false;
+ } else {
+ err = mSource->read(&mInputBuffer, &options);
+ }
CHECK((err == OK && mInputBuffer != NULL)
|| (err != OK && mInputBuffer == NULL));
diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
index 2bc4448..8ae1135 100644
--- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp
+++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp
@@ -15,6 +15,7 @@
*/
#include "AACDecoder.h"
+#define LOG_TAG "AACDecoder"
#include "../../include/ESDS.h"
@@ -36,26 +37,33 @@ AACDecoder::AACDecoder(const sp<MediaSource> &source)
mAnchorTimeUs(0),
mNumSamplesOutput(0),
mInputBuffer(NULL) {
-}
-AACDecoder::~AACDecoder() {
- if (mStarted) {
- stop();
- }
+ sp<MetaData> srcFormat = mSource->getFormat();
- delete mConfig;
- mConfig = NULL;
-}
+ int32_t sampleRate;
+ CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate));
-status_t AACDecoder::start(MetaData *params) {
- CHECK(!mStarted);
+ mMeta = new MetaData;
+ mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
- mBufferGroup = new MediaBufferGroup;
- mBufferGroup->add_buffer(new MediaBuffer(2048 * 2));
+ // We'll always output stereo, regardless of how many channels are
+ // present in the input due to decoder limitations.
+ mMeta->setInt32(kKeyChannelCount, 2);
+ mMeta->setInt32(kKeySampleRate, sampleRate);
+ int64_t durationUs;
+ if (srcFormat->findInt64(kKeyDuration, &durationUs)) {
+ mMeta->setInt64(kKeyDuration, durationUs);
+ }
+ mMeta->setCString(kKeyDecoderComponent, "AACDecoder");
+
+ mInitCheck = initCheck();
+}
+
+status_t AACDecoder::initCheck() {
+ memset(mConfig, 0, sizeof(tPVMP4AudioDecoderExternal));
mConfig->outputFormat = OUTPUTFORMAT_16PCM_INTERLEAVED;
- mConfig->aacPlusUpsamplingFactor = 0;
- mConfig->aacPlusEnabled = false;
+ mConfig->aacPlusEnabled = 1;
// The software decoder doesn't properly support mono output on
// AACplus files. Always output stereo.
@@ -64,8 +72,11 @@ status_t AACDecoder::start(MetaData *params) {
UInt32 memRequirements = PVMP4AudioDecoderGetMemRequirements();
mDecoderBuf = malloc(memRequirements);
- CHECK_EQ(PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf),
- MP4AUDEC_SUCCESS);
+ status_t err = PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf);
+ if (err != MP4AUDEC_SUCCESS) {
+ LOGE("Failed to initialize MP4 audio decoder");
+ return UNKNOWN_ERROR;
+ }
uint32_t type;
const void *data;
@@ -83,18 +94,29 @@ status_t AACDecoder::start(MetaData *params) {
mConfig->pInputBuffer = (UChar *)codec_specific_data;
mConfig->inputBufferCurrentLength = codec_specific_data_size;
mConfig->inputBufferMaxLength = 0;
- mConfig->inputBufferUsedLength = 0;
- mConfig->remainderBits = 0;
-
- mConfig->pOutputBuffer = NULL;
- mConfig->pOutputBuffer_plus = NULL;
- mConfig->repositionFlag = false;
if (PVMP4AudioDecoderConfig(mConfig, mDecoderBuf)
!= MP4AUDEC_SUCCESS) {
return ERROR_UNSUPPORTED;
}
}
+ return OK;
+}
+
+AACDecoder::~AACDecoder() {
+ if (mStarted) {
+ stop();
+ }
+
+ delete mConfig;
+ mConfig = NULL;
+}
+
+status_t AACDecoder::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ mBufferGroup = new MediaBufferGroup;
+ mBufferGroup->add_buffer(new MediaBuffer(4096 * 2));
mSource->start();
@@ -127,28 +149,7 @@ status_t AACDecoder::stop() {
}
sp<MetaData> AACDecoder::getFormat() {
- sp<MetaData> srcFormat = mSource->getFormat();
-
- int32_t sampleRate;
- CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate));
-
- sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
-
- // We'll always output stereo, regardless of how many channels are
- // present in the input due to decoder limitations.
- meta->setInt32(kKeyChannelCount, 2);
-
- meta->setInt32(kKeySampleRate, sampleRate);
-
- int64_t durationUs;
- if (srcFormat->findInt64(kKeyDuration, &durationUs)) {
- meta->setInt64(kKeyDuration, durationUs);
- }
-
- meta->setCString(kKeyDecoderComponent, "AACDecoder");
-
- return meta;
+ return mMeta;
}
status_t AACDecoder::read(
@@ -200,13 +201,32 @@ status_t AACDecoder::read(
mConfig->remainderBits = 0;
mConfig->pOutputBuffer = static_cast<Int16 *>(buffer->data());
- mConfig->pOutputBuffer_plus = NULL;
+ mConfig->pOutputBuffer_plus = &mConfig->pOutputBuffer[2048];
mConfig->repositionFlag = false;
Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf);
+ // Check on the sampling rate to see whether it is changed.
+ int32_t sampleRate;
+ CHECK(mMeta->findInt32(kKeySampleRate, &sampleRate));
+ if (mConfig->samplingRate != sampleRate) {
+ mMeta->setInt32(kKeySampleRate, mConfig->samplingRate);
+ LOGW("Sample rate was %d, but now is %d",
+ sampleRate, mConfig->samplingRate);
+ buffer->release();
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ return INFO_FORMAT_CHANGED;
+ }
+
size_t numOutBytes =
mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels;
+ if (mConfig->aacPlusUpsamplingFactor == 2) {
+ if (mConfig->desiredChannels == 1) {
+ memcpy(&mConfig->pOutputBuffer[1024], &mConfig->pOutputBuffer[2048], numOutBytes * 2);
+ }
+ numOutBytes *= 2;
+ }
if (decoderErr != MP4AUDEC_SUCCESS) {
LOGW("AAC decoder returned error %d, substituting silence", decoderErr);
diff --git a/media/libstagefright/include/AACDecoder.h b/media/libstagefright/include/AACDecoder.h
index f09addd..200f93c 100644
--- a/media/libstagefright/include/AACDecoder.h
+++ b/media/libstagefright/include/AACDecoder.h
@@ -25,6 +25,7 @@ struct tPVMP4AudioDecoderExternal;
namespace android {
struct MediaBufferGroup;
+struct MetaData;
struct AACDecoder : public MediaSource {
AACDecoder(const sp<MediaSource> &source);
@@ -41,6 +42,7 @@ protected:
virtual ~AACDecoder();
private:
+ sp<MetaData> mMeta;
sp<MediaSource> mSource;
bool mStarted;
@@ -50,9 +52,11 @@ private:
void *mDecoderBuf;
int64_t mAnchorTimeUs;
int64_t mNumSamplesOutput;
+ status_t mInitCheck;
MediaBuffer *mInputBuffer;
+ status_t initCheck();
AACDecoder(const AACDecoder &);
AACDecoder &operator=(const AACDecoder &);
};
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 2b4714d..dab7601 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -49,6 +49,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.HashSet;
import java.util.List;
/**
@@ -67,11 +68,29 @@ public class DatabaseHelper extends SQLiteOpenHelper {
private Context mContext;
+ private static final HashSet<String> mValidTables = new HashSet<String>();
+
+ static {
+ mValidTables.add("system");
+ mValidTables.add("secure");
+ mValidTables.add("bluetooth_devices");
+ mValidTables.add("bookmarks");
+
+ // These are old.
+ mValidTables.add("favorites");
+ mValidTables.add("gservices");
+ mValidTables.add("old_favorites");
+ }
+
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
}
+ public static boolean isValidTable(String name) {
+ return mValidTables.contains(name);
+ }
+
private void createSecureTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE secure (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1b4ba81..4372cd8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -83,6 +83,9 @@ public class SettingsProvider extends ContentProvider {
SqlArguments(Uri url, String where, String[] args) {
if (url.getPathSegments().size() == 1) {
this.table = url.getPathSegments().get(0);
+ if (!DatabaseHelper.isValidTable(this.table)) {
+ throw new IllegalArgumentException("Bad root path: " + this.table);
+ }
this.where = where;
this.args = args;
} else if (url.getPathSegments().size() != 2) {
@@ -91,6 +94,9 @@ public class SettingsProvider extends ContentProvider {
throw new UnsupportedOperationException("WHERE clause not supported: " + url);
} else {
this.table = url.getPathSegments().get(0);
+ if (!DatabaseHelper.isValidTable(this.table)) {
+ throw new IllegalArgumentException("Bad root path: " + this.table);
+ }
if ("system".equals(this.table) || "secure".equals(this.table)) {
this.where = Settings.NameValueTable.NAME + "=?";
this.args = new String[] { url.getPathSegments().get(1) };
@@ -105,6 +111,9 @@ public class SettingsProvider extends ContentProvider {
SqlArguments(Uri url) {
if (url.getPathSegments().size() == 1) {
this.table = url.getPathSegments().get(0);
+ if (!DatabaseHelper.isValidTable(this.table)) {
+ throw new IllegalArgumentException("Bad root path: " + this.table);
+ }
this.where = null;
this.args = null;
} else {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index d67dde0..6e307a5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -1313,7 +1313,7 @@ class BackupManagerService extends IBackupManager.Stub {
// If everything actually went through and this is the first time we've
// done a backup, we can now record what the current backup dataset token
// is.
- if ((mCurrentToken == 0) && (status != BackupConstants.TRANSPORT_OK)) {
+ if ((mCurrentToken == 0) && (status == BackupConstants.TRANSPORT_OK)) {
try {
mCurrentToken = mTransport.getCurrentRestoreSet();
} catch (RemoteException e) { /* cannot happen */ }
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 49d2a76..0b84c8d 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -6222,11 +6222,10 @@ class PackageManagerService extends IPackageManager.Stub {
File dataDir = new File(pkg.applicationInfo.dataDir);
dataDir.delete();
}
+ schedulePackageCleaning(packageName);
}
synchronized (mPackages) {
if (deletedPs != null) {
- schedulePackageCleaning(packageName);
-
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
if (outInfo != null) {
outInfo.removedUid = mSettings.removePackageLP(packageName);
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 493a348..7bbc32f 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -1022,36 +1022,71 @@ class PowerManagerService extends IPowerManager.Stub
}
}
- private void setTimeoutLocked(long now, int nextState)
- {
+ private void setTimeoutLocked(long now, int nextState) {
+ setTimeoutLocked(now, -1, nextState);
+ }
+
+ // If they gave a timeoutOverride it is the number of seconds
+ // to screen-off. Figure out where in the countdown cycle we
+ // should jump to.
+ private void setTimeoutLocked(long now, long timeoutOverride, int nextState) {
if (mBootCompleted) {
- mHandler.removeCallbacks(mTimeoutTask);
- mTimeoutTask.nextState = nextState;
- long when = now;
- switch (nextState)
- {
- case SCREEN_BRIGHT:
- when += mKeylightDelay;
- break;
- case SCREEN_DIM:
- if (mDimDelay >= 0) {
- when += mDimDelay;
- break;
- } else {
- Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
+ synchronized (mLocks) {
+ mHandler.removeCallbacks(mTimeoutTask);
+ mTimeoutTask.nextState = nextState;
+ long when = 0;
+ if (timeoutOverride <= 0) {
+ switch (nextState)
+ {
+ case SCREEN_BRIGHT:
+ when = now + mKeylightDelay;
+ break;
+ case SCREEN_DIM:
+ if (mDimDelay >= 0) {
+ when = now + mDimDelay;
+ break;
+ } else {
+ Slog.w(TAG, "mDimDelay=" + mDimDelay + " while trying to dim");
+ }
+ case SCREEN_OFF:
+ synchronized (mLocks) {
+ when = now + mScreenOffDelay;
+ }
+ break;
+ default:
+ when = now;
+ break;
}
- case SCREEN_OFF:
- synchronized (mLocks) {
- when += mScreenOffDelay;
+ } else {
+ override: {
+ if (timeoutOverride <= mScreenOffDelay) {
+ when = now + timeoutOverride;
+ nextState = SCREEN_OFF;
+ break override;
+ }
+ timeoutOverride -= mScreenOffDelay;
+
+ if (mDimDelay >= 0) {
+ if (timeoutOverride <= mDimDelay) {
+ when = now + timeoutOverride;
+ nextState = SCREEN_DIM;
+ break override;
+ }
+ timeoutOverride -= mDimDelay;
+ }
+
+ when = now + timeoutOverride;
+ nextState = SCREEN_BRIGHT;
}
- break;
- }
- if (mSpew) {
- Slog.d(TAG, "setTimeoutLocked now=" + now + " nextState=" + nextState
- + " when=" + when);
+ }
+ if (mSpew) {
+ Slog.d(TAG, "setTimeoutLocked now=" + now
+ + " timeoutOverride=" + timeoutOverride
+ + " nextState=" + nextState + " when=" + when);
+ }
+ mHandler.postAtTime(mTimeoutTask, when);
+ mNextTimeout = when; // for debugging
}
- mHandler.postAtTime(mTimeoutTask, when);
- mNextTimeout = when; // for debugging
}
}
@@ -1958,18 +1993,33 @@ class PowerManagerService extends IPowerManager.Stub
public void userActivityWithForce(long time, boolean noChangeLights, boolean force) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
- userActivity(time, noChangeLights, OTHER_EVENT, force);
+ userActivity(time, -1, noChangeLights, OTHER_EVENT, force);
}
public void userActivity(long time, boolean noChangeLights) {
- userActivity(time, noChangeLights, OTHER_EVENT, false);
+ userActivity(time, -1, noChangeLights, OTHER_EVENT, false);
}
public void userActivity(long time, boolean noChangeLights, int eventType) {
- userActivity(time, noChangeLights, eventType, false);
+ userActivity(time, -1, noChangeLights, eventType, false);
}
public void userActivity(long time, boolean noChangeLights, int eventType, boolean force) {
+ userActivity(time, -1, noChangeLights, eventType, force);
+ }
+
+ /*
+ * Reset the user activity timeout to now + timeout. This overrides whatever else is going
+ * on with user activity. Don't use this function.
+ */
+ public void clearUserActivityTimeout(long now, long timeout) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+ Slog.i(TAG, "clearUserActivity for " + timeout + "ms from now");
+ userActivity(now, timeout, false, OTHER_EVENT, false);
+ }
+
+ private void userActivity(long time, long timeoutOverride, boolean noChangeLights,
+ int eventType, boolean force) {
//mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
if (((mPokey & POKE_LOCK_IGNORE_CHEEK_EVENTS) != 0)
@@ -2041,7 +2091,7 @@ class PowerManagerService extends IPowerManager.Stub
mWakeLockState = mLocks.reactivateScreenLocksLocked();
setPowerState(mUserState | mWakeLockState, noChangeLights,
WindowManagerPolicy.OFF_BECAUSE_OF_USER);
- setTimeoutLocked(time, SCREEN_BRIGHT);
+ setTimeoutLocked(time, timeoutOverride, SCREEN_BRIGHT);
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/DropBoxTest.java b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
index 78a90fb..f3baff4 100644
--- a/services/tests/servicestests/src/com/android/server/DropBoxTest.java
+++ b/services/tests/servicestests/src/com/android/server/DropBoxTest.java
@@ -20,6 +20,10 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.os.DropBoxManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.ServiceManager;
import android.os.StatFs;
import android.provider.Settings;
@@ -27,10 +31,13 @@ import android.test.AndroidTestCase;
import com.android.server.DropBoxManagerService;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
+import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.util.Random;
import java.util.zip.GZIPOutputStream;
@@ -531,6 +538,203 @@ public class DropBoxTest extends AndroidTestCase {
service.stop();
}
+ public void testDropBoxEntrySerialization() throws Exception {
+ // Make sure DropBoxManager.Entry can be serialized to a Parcel and back
+ // under a variety of conditions.
+
+ Parcel parcel = Parcel.obtain();
+ File dir = getEmptyDir("testDropBoxEntrySerialization");
+
+ new DropBoxManager.Entry("empty", 1000000).writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("string", 2000000, "String Value").writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("bytes", 3000000, "Bytes Value".getBytes(),
+ DropBoxManager.IS_TEXT).writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("zerobytes", 4000000, new byte[0], 0).writeToParcel(parcel, 0);
+ new DropBoxManager.Entry("emptybytes", 5000000, (byte[]) null,
+ DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
+
+ try {
+ new DropBoxManager.Entry("badbytes", 99999,
+ "Bad Bytes Value".getBytes(),
+ DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
+ fail("IllegalArgumentException expected for non-null byte[] and IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ new DropBoxManager.Entry("badbytes", 99999, (byte[]) null, 0).writeToParcel(parcel, 0);
+ fail("IllegalArgumentException expected for null byte[] and non-IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ File f = new File(dir, "file.dat");
+ FileOutputStream os = new FileOutputStream(f);
+ os.write("File Value".getBytes());
+ os.close();
+
+ new DropBoxManager.Entry("file", 6000000, f, DropBoxManager.IS_TEXT).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ new DropBoxManager.Entry("binfile", 7000000, f, 0).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ new DropBoxManager.Entry("emptyfile", 8000000, (ParcelFileDescriptor) null,
+ DropBoxManager.IS_EMPTY).writeToParcel(parcel, 0);
+
+ try {
+ new DropBoxManager.Entry("badfile", 99999, new File(dir, "nonexist.dat"), 0);
+ fail("IOException expected for nonexistent file");
+ } catch (IOException e) {
+ // expected
+ }
+
+ try {
+ new DropBoxManager.Entry("badfile", 99999, f, DropBoxManager.IS_EMPTY).writeToParcel(
+ parcel, 0);
+ fail("IllegalArgumentException expected for non-null file and IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ new DropBoxManager.Entry("badfile", 99999, (ParcelFileDescriptor) null, 0);
+ fail("IllegalArgumentException expected for null PFD and non-IS_EMPTY flags");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ File gz = new File(dir, "file.gz");
+ GZIPOutputStream gzout = new GZIPOutputStream(new FileOutputStream(gz));
+ gzout.write("Gzip File Value".getBytes());
+ gzout.close();
+
+ new DropBoxManager.Entry("gzipfile", 9000000, gz,
+ DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ new DropBoxManager.Entry("gzipbinfile", 10000000, gz,
+ DropBoxManager.IS_GZIPPED).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+
+ //
+ // Switch from writing to reading
+ //
+
+ parcel.setDataPosition(0);
+ DropBoxManager.Entry e;
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("empty", e.getTag());
+ assertEquals(1000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null, e.getInputStream());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("string", e.getTag());
+ assertEquals(2000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("String Value", e.getText(100));
+ assertEquals("String Value",
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("bytes", e.getTag());
+ assertEquals(3000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("Bytes Value", e.getText(100));
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("zerobytes", e.getTag());
+ assertEquals(4000000, e.getTimeMillis());
+ assertEquals(0, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null,
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("emptybytes", e.getTag());
+ assertEquals(5000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null, e.getInputStream());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("file", e.getTag());
+ assertEquals(6000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("File Value", e.getText(100));
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("binfile", e.getTag());
+ assertEquals(7000000, e.getTimeMillis());
+ assertEquals(0, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals("File Value",
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("emptyfile", e.getTag());
+ assertEquals(8000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_EMPTY, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals(null, e.getInputStream());
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("gzipfile", e.getTag());
+ assertEquals(9000000, e.getTimeMillis());
+ assertEquals(DropBoxManager.IS_TEXT, e.getFlags());
+ assertEquals("Gzip File Value", e.getText(100));
+ e.close();
+
+ e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("gzipbinfile", e.getTag());
+ assertEquals(10000000, e.getTimeMillis());
+ assertEquals(0, e.getFlags());
+ assertEquals(null, e.getText(100));
+ assertEquals("Gzip File Value",
+ new BufferedReader(new InputStreamReader(e.getInputStream())).readLine());
+ e.close();
+
+ assertEquals(0, parcel.dataAvail());
+ parcel.recycle();
+ }
+
+ public void testDropBoxEntrySerializationDoesntLeakFileDescriptors() throws Exception {
+ File dir = getEmptyDir("testDropBoxEntrySerialization");
+ File f = new File(dir, "file.dat");
+ FileOutputStream os = new FileOutputStream(f);
+ os.write("File Value".getBytes());
+ os.close();
+
+ int before = countOpenFiles();
+ assertTrue(before > 0);
+
+ for (int i = 0; i < 1000; i++) {
+ Parcel parcel = Parcel.obtain();
+ new DropBoxManager.Entry("file", 1000000, f, 0).writeToParcel(
+ parcel, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+
+ parcel.setDataPosition(0);
+ DropBoxManager.Entry e = DropBoxManager.Entry.CREATOR.createFromParcel(parcel);
+ assertEquals("file", e.getTag());
+ e.close();
+
+ parcel.recycle();
+ }
+
+ int after = countOpenFiles();
+ assertTrue(after > 0);
+ assertTrue(after < before + 20);
+ }
+
private void addRandomEntry(DropBoxManager dropbox, String tag, int size) throws Exception {
byte[] bytes = new byte[size];
new Random(System.currentTimeMillis()).nextBytes(bytes);
@@ -564,4 +768,8 @@ public class DropBoxTest extends AndroidTestCase {
assertTrue(dir.listFiles().length == 0);
return dir;
}
+
+ private int countOpenFiles() {
+ return new File("/proc/" + Process.myPid() + "/fd").listFiles().length;
+ }
}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 32e7176..55d25a5 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -135,9 +135,9 @@ public class PhoneNumberUtils
}
// TODO: We don't check for SecurityException here (requires
- // READ_PHONE_STATE permission).
+ // CALL_PRIVILEGED permission).
if (scheme.equals("voicemail")) {
- return TelephonyManager.getDefault().getVoiceMailNumber();
+ return TelephonyManager.getDefault().getCompleteVoiceMailNumber();
}
if (context == null) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f018d10..4ee9560 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -659,6 +659,25 @@ public class TelephonyManager {
}
/**
+ * Returns the complete voice mail number. Return null if it is unavailable.
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#CALL_PRIVILEGED CALL_PRIVILEGED}
+ *
+ * @hide
+ */
+ public String getCompleteVoiceMailNumber() {
+ try {
+ return getSubscriberInfo().getCompleteVoiceMailNumber();
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone restarts due to crashing
+ return null;
+ }
+ }
+
+ /**
* Returns the voice mail count. Return 0 if unavailable.
* <p>
* Requires Permission:
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index e74b9e4..5cba2e1 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -59,6 +59,11 @@ interface IPhoneSubInfo {
String getVoiceMailNumber();
/**
+ * Retrieves the complete voice mail number.
+ */
+ String getCompleteVoiceMailNumber();
+
+ /**
* Retrieves the alpha identifier associated with the voice mail number.
*/
String getVoiceMailAlphaTag();
diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
index 1ac2da3..0b55736 100644
--- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
+++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
@@ -21,6 +21,7 @@ import java.io.PrintWriter;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
+import android.telephony.PhoneNumberUtils;
import android.util.Log;
public class PhoneSubInfo extends IPhoneSubInfo.Stub {
@@ -29,6 +30,9 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
private Context mContext;
private static final String READ_PHONE_STATE =
android.Manifest.permission.READ_PHONE_STATE;
+ private static final String CALL_PRIVILEGED =
+ // TODO Add core/res/AndriodManifest.xml#READ_PRIVILEGED_PHONE_STATE
+ android.Manifest.permission.CALL_PRIVILEGED;
public PhoneSubInfo(Phone phone) {
mPhone = phone;
@@ -101,7 +105,22 @@ public class PhoneSubInfo extends IPhoneSubInfo.Stub {
*/
public String getVoiceMailNumber() {
mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
- return (String) mPhone.getVoiceMailNumber();
+ String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber());
+ Log.d(LOG_TAG, "VM: PhoneSubInfo.getVoiceMailNUmber: "); // + number);
+ return number;
+ }
+
+ /**
+ * Retrieves the compelete voice mail number.
+ *
+ * @hide
+ */
+ public String getCompleteVoiceMailNumber() {
+ mContext.enforceCallingOrSelfPermission(CALL_PRIVILEGED,
+ "Requires CALL_PRIVILEGED");
+ String number = mPhone.getVoiceMailNumber();
+ Log.d(LOG_TAG, "VM: PhoneSubInfo.getCompleteVoiceMailNUmber: "); // + number);
+ return number;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
index adfbe20..a036046 100644
--- a/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneSubInfoProxy.java
@@ -82,6 +82,13 @@ public class PhoneSubInfoProxy extends IPhoneSubInfo.Stub {
}
/**
+ * Retrieves the complete voice mail number.
+ */
+ public String getCompleteVoiceMailNumber() {
+ return mPhoneSubInfo.getCompleteVoiceMailNumber();
+ }
+
+ /**
* Retrieves the alpha identifier associated with the voice mail number.
*/
public String getVoiceMailAlphaTag() {