Merge change Ie417158c
* changes:
fix [2330211] sensors thread priority too low
diff --git a/Android.mk b/Android.mk
index f9cf6dd..0cf1229 100644
--- a/Android.mk
+++ b/Android.mk
@@ -361,33 +361,31 @@
web_docs_sample_code_flags := \
-hdf android.hasSamples 1 \
-samplecode $(sample_dir)/ApiDemos \
- guide/samples/ApiDemos "API Demos" \
- -samplecode $(sample_dir)/BluetoothChat \
- guide/samples/BluetoothChat "Bluetooth Chat" \
- -samplecode $(sample_dir)/ContactManager \
- guide/samples/ContactManager "Contact Manager" \
+ resources/samples/ApiDemos "API Demos" \
+ -samplecode $(sample_dir)/BluetoothChat \
+ resources/samples/BluetoothChat "Bluetooth Chat" \
+ -samplecode $(sample_dir)/ContactManager \
+ resources/samples/ContactManager "Contact Manager" \
-samplecode $(sample_dir)/Home \
- guide/samples/Home "Home" \
+ resources/samples/Home "Home" \
-samplecode $(sample_dir)/JetBoy \
- guide/samples/JetBoy "JetBoy" \
+ resources/samples/JetBoy "JetBoy" \
-samplecode $(sample_dir)/LunarLander \
- guide/samples/LunarLander "Lunar Lander" \
- -samplecode $(sample_dir)/MultiResolution \
- guide/samples/MultiResolution "Multiple Resolutions" \
+ resources/samples/LunarLander "Lunar Lander" \
+ -samplecode $(sample_dir)/MultiResolution \
+ resources/samples/MultiResolution "Multiple Resolutions" \
-samplecode $(sample_dir)/NotePad \
- guide/samples/NotePad "Note Pad" \
+ resources/samples/NotePad "Note Pad" \
-samplecode $(sample_dir)/SearchableDictionary \
- guide/samples/SearchableDictionary "Searchable Dictionary" \
+ resources/samples/SearchableDictionary "Searchable Dictionary" \
-samplecode $(sample_dir)/Snake \
- guide/samples/Snake "Snake" \
+ resources/samples/Snake "Snake" \
-samplecode $(sample_dir)/SoftKeyboard \
- guide/samples/SoftKeyboard "Soft Keyboard" \
- -samplecode $(sample_dir)/Wiktionary \
- guide/samples/Wiktionary "Wiktionary" \
- -samplecode $(sample_dir)/WiktionarySimple \
- guide/samples/WiktionarySimple "Wiktionary (Simplified)"
-
-
+ resources/samples/SoftKeyboard "Soft Keyboard" \
+ -samplecode $(sample_dir)/Wiktionary \
+ resources/samples/Wiktionary "Wiktionary" \
+ -samplecode $(sample_dir)/WiktionarySimple \
+ resources/samples/WiktionarySimple "Wiktionary (Simplified)"
## SDK version identifiers used in the published docs
# major[.minor] version for current SDK. (full releases only)
diff --git a/api/current.xml b/api/current.xml
index 346aed7..b6fb64b 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -28148,6 +28148,19 @@
<parameter name="syncResult" type="android.content.SyncResult">
</parameter>
</method>
+<method name="onSyncCanceled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="thread" type="java.lang.Thread">
+</parameter>
+</method>
<field name="LOG_SYNC_DETAILS"
type="int"
transient="false"
diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java
deleted file mode 100644
index 5903c83..0000000
--- a/core/java/android/content/AbstractSyncableContentProvider.java
+++ /dev/null
@@ -1,758 +0,0 @@
-package android.content;
-
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.Cursor;
-import android.net.Uri;
-import android.accounts.OnAccountsUpdateListener;
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.provider.SyncConstValue;
-import android.util.Config;
-import android.util.Log;
-import android.os.Bundle;
-import android.text.TextUtils;
-
-import java.util.Collections;
-import java.util.Map;
-import java.util.Vector;
-import java.util.ArrayList;
-import java.util.Set;
-import java.util.HashSet;
-
-import com.google.android.collect.Maps;
-
-/**
- * A specialization of the ContentProvider that centralizes functionality
- * used by ContentProviders that are syncable. It also wraps calls to the ContentProvider
- * inside of database transactions.
- *
- * @hide
- */
-public abstract class AbstractSyncableContentProvider extends SyncableContentProvider {
- private static final String TAG = "SyncableContentProvider";
- protected SQLiteOpenHelper mOpenHelper;
- protected SQLiteDatabase mDb;
- private final String mDatabaseName;
- private final int mDatabaseVersion;
- private final Uri mContentUri;
-
- /** the account set in the last call to onSyncStart() */
- private Account mSyncingAccount;
-
- private SyncStateContentProviderHelper mSyncState = null;
-
- private static final String[] sAccountProjection =
- new String[] {SyncConstValue._SYNC_ACCOUNT, SyncConstValue._SYNC_ACCOUNT_TYPE};
-
- private boolean mIsTemporary;
-
- private AbstractTableMerger mCurrentMerger = null;
- private boolean mIsMergeCancelled = false;
-
- private static final String SYNC_ACCOUNT_WHERE_CLAUSE =
- SyncConstValue._SYNC_ACCOUNT + "=? AND " + SyncConstValue._SYNC_ACCOUNT_TYPE + "=?";
-
- protected boolean isTemporary() {
- return mIsTemporary;
- }
-
- private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>();
- private final ThreadLocal<Set<Uri>> mPendingBatchNotifications = new ThreadLocal<Set<Uri>>();
-
- /**
- * Indicates whether or not this ContentProvider contains a full
- * set of data or just diffs. This knowledge comes in handy when
- * determining how to incorporate the contents of a temporary
- * provider into a real provider.
- */
- private boolean mContainsDiffs;
-
- /**
- * Initializes the AbstractSyncableContentProvider
- * @param dbName the filename of the database
- * @param dbVersion the current version of the database schema
- * @param contentUri The base Uri of the syncable content in this provider
- */
- public AbstractSyncableContentProvider(String dbName, int dbVersion, Uri contentUri) {
- super();
-
- mDatabaseName = dbName;
- mDatabaseVersion = dbVersion;
- mContentUri = contentUri;
- mIsTemporary = false;
- setContainsDiffs(false);
- if (Config.LOGV) {
- Log.v(TAG, "created SyncableContentProvider " + this);
- }
- }
-
- /**
- * Close resources that must be closed. You must call this to properly release
- * the resources used by the AbstractSyncableContentProvider.
- */
- public void close() {
- if (mOpenHelper != null) {
- mOpenHelper.close(); // OK to call .close() repeatedly.
- }
- }
-
- /**
- * Override to create your schema and do anything else you need to do with a new database.
- * This is run inside a transaction (so you don't need to use one).
- * This method may not use getDatabase(), or call content provider methods, it must only
- * use the database handle passed to it.
- */
- protected void bootstrapDatabase(SQLiteDatabase db) {}
-
- /**
- * Override to upgrade your database from an old version to the version you specified.
- * Don't set the DB version; this will automatically be done after the method returns.
- * This method may not use getDatabase(), or call content provider methods, it must only
- * use the database handle passed to it.
- *
- * @param oldVersion version of the existing database
- * @param newVersion current version to upgrade to
- * @return true if the upgrade was lossless, false if it was lossy
- */
- protected abstract boolean upgradeDatabase(SQLiteDatabase db, int oldVersion, int newVersion);
-
- /**
- * Override to do anything (like cleanups or checks) you need to do after opening a database.
- * Does nothing by default. This is run inside a transaction (so you don't need to use one).
- * This method may not use getDatabase(), or call content provider methods, it must only
- * use the database handle passed to it.
- */
- protected void onDatabaseOpened(SQLiteDatabase db) {}
-
- private class DatabaseHelper extends SQLiteOpenHelper {
- DatabaseHelper(Context context, String name) {
- // Note: context and name may be null for temp providers
- super(context, name, null, mDatabaseVersion);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- bootstrapDatabase(db);
- mSyncState.createDatabase(db);
- if (!isTemporary()) {
- ContentResolver.requestSync(null /* all accounts */,
- mContentUri.getAuthority(), new Bundle());
- }
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (!upgradeDatabase(db, oldVersion, newVersion)) {
- mSyncState.discardSyncData(db, null /* all accounts */);
- ContentResolver.requestSync(null /* all accounts */,
- mContentUri.getAuthority(), new Bundle());
- }
- }
-
- @Override
- public void onOpen(SQLiteDatabase db) {
- onDatabaseOpened(db);
- mSyncState.onDatabaseOpened(db);
- }
- }
-
- @Override
- public boolean onCreate() {
- if (isTemporary()) throw new IllegalStateException("onCreate() called for temp provider");
- mOpenHelper = new AbstractSyncableContentProvider.DatabaseHelper(getContext(),
- mDatabaseName);
- mSyncState = new SyncStateContentProviderHelper(mOpenHelper);
- AccountManager.get(getContext()).addOnAccountsUpdatedListener(
- new OnAccountsUpdateListener() {
- public void onAccountsUpdated(Account[] accounts) {
- // Some providers override onAccountsChanged(); give them a database to
- // work with.
- mDb = mOpenHelper.getWritableDatabase();
- // Only call onAccountsChanged on GAIA accounts; otherwise, the contacts and
- // calendar providers will choke as they try to sync unknown accounts with
- // AbstractGDataSyncAdapter, which will put acore into a crash loop
- ArrayList<Account> gaiaAccounts = new ArrayList<Account>();
- for (Account acct: accounts) {
- if (acct.type.equals("com.google")) {
- gaiaAccounts.add(acct);
- }
- }
- accounts = new Account[gaiaAccounts.size()];
- int i = 0;
- for (Account acct: gaiaAccounts) {
- accounts[i++] = acct;
- }
- onAccountsChanged(accounts);
- TempProviderSyncAdapter syncAdapter = getTempProviderSyncAdapter();
- if (syncAdapter != null) {
- syncAdapter.onAccountsChanged(accounts);
- }
- }
- }, null /* handler */, true /* updateImmediately */);
-
- return true;
- }
- /**
- * Get a non-persistent instance of this content provider.
- * You must call {@link #close} on the returned
- * SyncableContentProvider when you are done with it.
- *
- * @return a non-persistent content provider with the same layout as this
- * provider.
- */
- public AbstractSyncableContentProvider getTemporaryInstance() {
- AbstractSyncableContentProvider temp;
- try {
- temp = getClass().newInstance();
- } catch (InstantiationException e) {
- throw new RuntimeException("unable to instantiate class, "
- + "this should never happen", e);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(
- "IllegalAccess while instantiating class, "
- + "this should never happen", e);
- }
-
- // Note: onCreate() isn't run for the temp provider, and it has no Context.
- temp.mIsTemporary = true;
- temp.setContainsDiffs(true);
- temp.mOpenHelper = temp.new DatabaseHelper(null, null);
- temp.mSyncState = new SyncStateContentProviderHelper(temp.mOpenHelper);
- if (!isTemporary()) {
- mSyncState.copySyncState(
- mOpenHelper.getReadableDatabase(),
- temp.mOpenHelper.getWritableDatabase(),
- getSyncingAccount());
- }
- return temp;
- }
-
- public SQLiteDatabase getDatabase() {
- if (mDb == null) mDb = mOpenHelper.getWritableDatabase();
- return mDb;
- }
-
- public boolean getContainsDiffs() {
- return mContainsDiffs;
- }
-
- public void setContainsDiffs(boolean containsDiffs) {
- if (containsDiffs && !isTemporary()) {
- throw new IllegalStateException(
- "only a temporary provider can contain diffs");
- }
- mContainsDiffs = containsDiffs;
- }
-
- /**
- * Each subclass of this class should define a subclass of {@link
- * android.content.AbstractTableMerger} for each table they wish to merge. It
- * should then override this method and return one instance of
- * each merger, in sequence. Their {@link
- * android.content.AbstractTableMerger#merge merge} methods will be called, one at a
- * time, in the order supplied.
- *
- * <p>The default implementation returns an empty list, so that no
- * merging will occur.
- * @return A sequence of subclasses of {@link
- * android.content.AbstractTableMerger}, one for each table that should be merged.
- */
- protected Iterable<? extends AbstractTableMerger> getMergers() {
- return Collections.emptyList();
- }
-
- @Override
- public final int update(final Uri url, final ContentValues values,
- final String selection, final String[] selectionArgs) {
- mDb = mOpenHelper.getWritableDatabase();
- final boolean notApplyingBatch = !applyingBatch();
- if (notApplyingBatch) {
- mDb.beginTransaction();
- }
- try {
- if (isTemporary() && mSyncState.matches(url)) {
- int numRows = mSyncState.asContentProvider().update(
- url, values, selection, selectionArgs);
- if (notApplyingBatch) {
- mDb.setTransactionSuccessful();
- }
- return numRows;
- }
-
- int result = updateInternal(url, values, selection, selectionArgs);
- if (notApplyingBatch) {
- mDb.setTransactionSuccessful();
- }
- if (!isTemporary() && result > 0) {
- if (notApplyingBatch) {
- getContext().getContentResolver().notifyChange(url, null /* observer */,
- changeRequiresLocalSync(url));
- } else {
- mPendingBatchNotifications.get().add(url);
- }
- }
- return result;
- } finally {
- if (notApplyingBatch) {
- mDb.endTransaction();
- }
- }
- }
-
- @Override
- public final int delete(final Uri url, final String selection,
- final String[] selectionArgs) {
- mDb = mOpenHelper.getWritableDatabase();
- final boolean notApplyingBatch = !applyingBatch();
- if (notApplyingBatch) {
- mDb.beginTransaction();
- }
- try {
- if (isTemporary() && mSyncState.matches(url)) {
- int numRows = mSyncState.asContentProvider().delete(url, selection, selectionArgs);
- if (notApplyingBatch) {
- mDb.setTransactionSuccessful();
- }
- return numRows;
- }
- int result = deleteInternal(url, selection, selectionArgs);
- if (notApplyingBatch) {
- mDb.setTransactionSuccessful();
- }
- if (!isTemporary() && result > 0) {
- if (notApplyingBatch) {
- getContext().getContentResolver().notifyChange(url, null /* observer */,
- changeRequiresLocalSync(url));
- } else {
- mPendingBatchNotifications.get().add(url);
- }
- }
- return result;
- } finally {
- if (notApplyingBatch) {
- mDb.endTransaction();
- }
- }
- }
-
- private boolean applyingBatch() {
- return mApplyingBatch.get() != null && mApplyingBatch.get();
- }
-
- @Override
- public final Uri insert(final Uri url, final ContentValues values) {
- mDb = mOpenHelper.getWritableDatabase();
- final boolean notApplyingBatch = !applyingBatch();
- if (notApplyingBatch) {
- mDb.beginTransaction();
- }
- try {
- if (isTemporary() && mSyncState.matches(url)) {
- Uri result = mSyncState.asContentProvider().insert(url, values);
- if (notApplyingBatch) {
- mDb.setTransactionSuccessful();
- }
- return result;
- }
- Uri result = insertInternal(url, values);
- if (notApplyingBatch) {
- mDb.setTransactionSuccessful();
- }
- if (!isTemporary() && result != null) {
- if (notApplyingBatch) {
- getContext().getContentResolver().notifyChange(url, null /* observer */,
- changeRequiresLocalSync(url));
- } else {
- mPendingBatchNotifications.get().add(url);
- }
- }
- return result;
- } finally {
- if (notApplyingBatch) {
- mDb.endTransaction();
- }
- }
- }
-
- @Override
- public final int bulkInsert(final Uri uri, final ContentValues[] values) {
- int size = values.length;
- int completed = 0;
- final boolean isSyncStateUri = mSyncState.matches(uri);
- mDb = mOpenHelper.getWritableDatabase();
- mDb.beginTransaction();
- try {
- for (int i = 0; i < size; i++) {
- Uri result;
- if (isTemporary() && isSyncStateUri) {
- result = mSyncState.asContentProvider().insert(uri, values[i]);
- } else {
- result = insertInternal(uri, values[i]);
- mDb.yieldIfContended();
- }
- if (result != null) {
- completed++;
- }
- }
- mDb.setTransactionSuccessful();
- } finally {
- mDb.endTransaction();
- }
- if (!isTemporary() && completed == size) {
- getContext().getContentResolver().notifyChange(uri, null /* observer */,
- changeRequiresLocalSync(uri));
- }
- return completed;
- }
-
- /**
- * <p>
- * Start batch transaction. {@link #endTransaction} MUST be called after
- * calling this method. Those methods should be used like this:
- * </p>
- *
- * <pre class="prettyprint">
- * boolean successful = false;
- * beginBatch()
- * try {
- * // Do something related to mDb
- * successful = true;
- * return ret;
- * } finally {
- * endBatch(successful);
- * }
- * </pre>
- *
- * @hide This method should be used only when {@link ContentProvider#applyBatch} is not enough and must be
- * used with {@link #endBatch}.
- * e.g. If returned value has to be used during one transaction, this method might be useful.
- */
- public final void beginBatch() {
- // initialize if this is the first time this thread has applied a batch
- if (mApplyingBatch.get() == null) {
- mApplyingBatch.set(false);
- mPendingBatchNotifications.set(new HashSet<Uri>());
- }
-
- if (applyingBatch()) {
- throw new IllegalStateException(
- "applyBatch is not reentrant but mApplyingBatch is already set");
- }
- SQLiteDatabase db = getDatabase();
- db.beginTransaction();
- boolean successful = false;
- try {
- mApplyingBatch.set(true);
- successful = true;
- } finally {
- if (!successful) {
- // Something unexpected happened. We must call endTransaction() at least.
- db.endTransaction();
- }
- }
- }
-
- /**
- * <p>
- * Finish batch transaction. If "successful" is true, try to call
- * mDb.setTransactionSuccessful() before calling mDb.endTransaction().
- * This method MUST be used with {@link #beginBatch()}.
- * </p>
- *
- * @hide This method must be used with {@link #beginTransaction}
- */
- public final void endBatch(boolean successful) {
- try {
- if (successful) {
- // setTransactionSuccessful() must be called just once during opening the
- // transaction.
- mDb.setTransactionSuccessful();
- }
- } finally {
- mApplyingBatch.set(false);
- getDatabase().endTransaction();
- for (Uri url : mPendingBatchNotifications.get()) {
- getContext().getContentResolver().notifyChange(url, null /* observer */,
- changeRequiresLocalSync(url));
- }
- }
- }
-
- public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
- throws OperationApplicationException {
- boolean successful = false;
- beginBatch();
- try {
- ContentProviderResult[] results = super.applyBatch(operations);
- successful = true;
- return results;
- } finally {
- endBatch(successful);
- }
- }
-
- /**
- * Check if changes to this URI can be syncable changes.
- * @param uri the URI of the resource that was changed
- * @return true if changes to this URI can be syncable changes, false otherwise
- */
- public boolean changeRequiresLocalSync(Uri uri) {
- return true;
- }
-
- @Override
- public final Cursor query(final Uri url, final String[] projection,
- final String selection, final String[] selectionArgs,
- final String sortOrder) {
- mDb = mOpenHelper.getReadableDatabase();
- if (isTemporary() && mSyncState.matches(url)) {
- return mSyncState.asContentProvider().query(
- url, projection, selection, selectionArgs, sortOrder);
- }
- return queryInternal(url, projection, selection, selectionArgs, sortOrder);
- }
-
- /**
- * Called right before a sync is started.
- *
- * @param context the sync context for the operation
- * @param account
- */
- public void onSyncStart(SyncContext context, Account account) {
- if (account == null) {
- throw new IllegalArgumentException("you passed in an empty account");
- }
- mSyncingAccount = account;
- }
-
- /**
- * Called right after a sync is completed
- *
- * @param context the sync context for the operation
- * @param success true if the sync succeeded, false if an error occurred
- */
- public void onSyncStop(SyncContext context, boolean success) {
- }
-
- /**
- * The account of the most recent call to onSyncStart()
- * @return the account
- */
- public Account getSyncingAccount() {
- return mSyncingAccount;
- }
-
- /**
- * Merge diffs from a sync source with this content provider.
- *
- * @param context the SyncContext within which this merge is taking place
- * @param diffs A temporary content provider containing diffs from a sync
- * source.
- * @param result a MergeResult that contains information about the merge, including
- * a temporary content provider with the same layout as this provider containing
- * @param syncResult
- */
- public void merge(SyncContext context, SyncableContentProvider diffs,
- TempProviderSyncResult result, SyncResult syncResult) {
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- db.beginTransaction();
- try {
- synchronized(this) {
- mIsMergeCancelled = false;
- }
- Iterable<? extends AbstractTableMerger> mergers = getMergers();
- try {
- for (AbstractTableMerger merger : mergers) {
- synchronized(this) {
- if (mIsMergeCancelled) break;
- mCurrentMerger = merger;
- }
- merger.merge(context, getSyncingAccount(), diffs, result, syncResult, this);
- }
- if (mIsMergeCancelled) return;
- if (diffs != null) {
- mSyncState.copySyncState(
- ((AbstractSyncableContentProvider)diffs).mOpenHelper.getReadableDatabase(),
- mOpenHelper.getWritableDatabase(),
- getSyncingAccount());
- }
- } finally {
- synchronized (this) {
- mCurrentMerger = null;
- }
- }
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
-
- /**
- * Invoked when the active sync has been canceled. Sets the sync state of this provider and
- * its merger to canceled.
- */
- public void onSyncCanceled() {
- synchronized (this) {
- mIsMergeCancelled = true;
- if (mCurrentMerger != null) {
- mCurrentMerger.onMergeCancelled();
- }
- }
- }
-
-
- public boolean isMergeCancelled() {
- return mIsMergeCancelled;
- }
-
- /**
- * Subclasses should override this instead of update(). See update()
- * for details.
- *
- * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
- * which means a database transaction will be active during the call;
- */
- protected abstract int updateInternal(Uri url, ContentValues values,
- String selection, String[] selectionArgs);
-
- /**
- * Subclasses should override this instead of delete(). See delete()
- * for details.
- *
- * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
- * which means a database transaction will be active during the call;
- */
- protected abstract int deleteInternal(Uri url, String selection, String[] selectionArgs);
-
- /**
- * Subclasses should override this instead of insert(). See insert()
- * for details.
- *
- * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
- * which means a database transaction will be active during the call;
- */
- protected abstract Uri insertInternal(Uri url, ContentValues values);
-
- /**
- * Subclasses should override this instead of query(). See query()
- * for details.
- *
- * <p> This method is *not* called within a acquireDbLock()/releaseDbLock()
- * block for performance reasons. If an implementation needs atomic access
- * to the database the lock can be acquired then.
- */
- protected abstract Cursor queryInternal(Uri url, String[] projection,
- String selection, String[] selectionArgs, String sortOrder);
-
- /**
- * Make sure that there are no entries for accounts that no longer exist
- * @param accountsArray the array of currently-existing accounts
- */
- protected void onAccountsChanged(Account[] accountsArray) {
- Map<Account, Boolean> accounts = Maps.newHashMap();
- for (Account account : accountsArray) {
- accounts.put(account, false);
- }
-
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- Map<String, String> tableMap = db.getSyncedTables();
- Vector<String> tables = new Vector<String>();
- tables.addAll(tableMap.keySet());
- tables.addAll(tableMap.values());
-
- db.beginTransaction();
- try {
- mSyncState.onAccountsChanged(accountsArray);
- for (String table : tables) {
- deleteRowsForRemovedAccounts(accounts, table);
- }
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * A helper method to delete all rows whose account is not in the accounts
- * map. The accountColumnName is the name of the column that is expected
- * to hold the account. If a row has an empty account it is never deleted.
- *
- * @param accounts a map of existing accounts
- * @param table the table to delete from
- */
- protected void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts, String table) {
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- Cursor c = db.query(table, sAccountProjection, null, null,
- "_sync_account, _sync_account_type", null, null);
- try {
- while (c.moveToNext()) {
- String accountName = c.getString(0);
- String accountType = c.getString(1);
- if (TextUtils.isEmpty(accountName)) {
- continue;
- }
- Account account = new Account(accountName, accountType);
- if (!accounts.containsKey(account)) {
- int numDeleted;
- numDeleted = db.delete(table, "_sync_account=? AND _sync_account_type=?",
- new String[]{account.name, account.type});
- if (Config.LOGV) {
- Log.v(TAG, "deleted " + numDeleted
- + " records from table " + table
- + " for account " + account);
- }
- }
- }
- } finally {
- c.close();
- }
- }
-
- /**
- * Called when the sync system determines that this provider should no longer
- * contain records for the specified account.
- */
- public void wipeAccount(Account account) {
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- Map<String, String> tableMap = db.getSyncedTables();
- ArrayList<String> tables = new ArrayList<String>();
- tables.addAll(tableMap.keySet());
- tables.addAll(tableMap.values());
-
- db.beginTransaction();
-
- try {
- // remove the SyncState data
- mSyncState.discardSyncData(db, account);
-
- // remove the data in the synced tables
- for (String table : tables) {
- db.delete(table, SYNC_ACCOUNT_WHERE_CLAUSE,
- new String[]{account.name, account.type});
- }
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Retrieves the SyncData bytes for the given account. The byte array returned may be null.
- */
- public byte[] readSyncDataBytes(Account account) {
- return mSyncState.readSyncDataBytes(mOpenHelper.getReadableDatabase(), account);
- }
-
- /**
- * Sets the SyncData bytes for the given account. The byte array may be null.
- */
- public void writeSyncDataBytes(Account account, byte[] data) {
- mSyncState.writeSyncDataBytes(mOpenHelper.getWritableDatabase(), account, data);
- }
-
- protected ContentProvider getSyncStateProvider() {
- return mSyncState.asContentProvider();
- }
-}
diff --git a/core/java/android/content/AbstractTableMerger.java b/core/java/android/content/AbstractTableMerger.java
deleted file mode 100644
index 9545fd7f..0000000
--- a/core/java/android/content/AbstractTableMerger.java
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright (C) 2006 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.content;
-
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.os.Debug;
-import android.provider.BaseColumns;
-import static android.provider.SyncConstValue.*;
-import android.text.TextUtils;
-import android.util.Log;
-import android.accounts.Account;
-
-/**
- * @hide
- */
-public abstract class AbstractTableMerger
-{
- private ContentValues mValues;
-
- protected SQLiteDatabase mDb;
- protected String mTable;
- protected Uri mTableURL;
- protected String mDeletedTable;
- protected Uri mDeletedTableURL;
- static protected ContentValues mSyncMarkValues;
- static private boolean TRACE;
-
- static {
- mSyncMarkValues = new ContentValues();
- mSyncMarkValues.put(_SYNC_MARK, 1);
- TRACE = false;
- }
-
- private static final String TAG = "AbstractTableMerger";
- private static final String[] syncDirtyProjection =
- new String[] {_SYNC_DIRTY, BaseColumns._ID, _SYNC_ID, _SYNC_VERSION};
- private static final String[] syncIdAndVersionProjection =
- new String[] {_SYNC_ID, _SYNC_VERSION};
-
- private volatile boolean mIsMergeCancelled;
-
- private static final String SELECT_MARKED = _SYNC_MARK + "> 0 and "
- + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?";
-
- private static final String SELECT_BY_SYNC_ID_AND_ACCOUNT =
- _SYNC_ID +"=? and " + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?";
- private static final String SELECT_BY_ID = BaseColumns._ID +"=?";
-
- private static final String SELECT_UNSYNCED =
- "(" + _SYNC_ACCOUNT + " IS NULL OR ("
- + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?)) and "
- + "(" + _SYNC_ID + " IS NULL OR (" + _SYNC_DIRTY + " > 0 and "
- + _SYNC_VERSION + " IS NOT NULL))";
-
- public AbstractTableMerger(SQLiteDatabase database,
- String table, Uri tableURL, String deletedTable,
- Uri deletedTableURL)
- {
- mDb = database;
- mTable = table;
- mTableURL = tableURL;
- mDeletedTable = deletedTable;
- mDeletedTableURL = deletedTableURL;
- mValues = new ContentValues();
- }
-
- public abstract void insertRow(ContentProvider diffs,
- Cursor diffsCursor);
- public abstract void updateRow(long localPersonID,
- ContentProvider diffs, Cursor diffsCursor);
- public abstract void resolveRow(long localPersonID,
- String syncID, ContentProvider diffs, Cursor diffsCursor);
-
- /**
- * This is called when it is determined that a row should be deleted from the
- * ContentProvider. The localCursor is on a table from the local ContentProvider
- * and its current position is of the row that should be deleted. The localCursor
- * is only guaranteed to contain the BaseColumns.ID column so the implementation
- * of deleteRow() must query the database directly if other columns are needed.
- * <p>
- * It is the responsibility of the implementation of this method to ensure that the cursor
- * points to the next row when this method returns, either by calling Cursor.deleteRow() or
- * Cursor.next().
- *
- * @param localCursor The Cursor into the local table, which points to the row that
- * is to be deleted.
- */
- public void deleteRow(Cursor localCursor) {
- localCursor.deleteRow();
- }
-
- /**
- * After {@link #merge} has completed, this method is called to send
- * notifications to {@link android.database.ContentObserver}s of changes
- * to the containing {@link ContentProvider}. These notifications likely
- * do not want to request a sync back to the network.
- */
- protected abstract void notifyChanges();
-
- private static boolean findInCursor(Cursor cursor, int column, String id) {
- while (!cursor.isAfterLast() && !cursor.isNull(column)) {
- int comp = id.compareTo(cursor.getString(column));
- if (comp > 0) {
- cursor.moveToNext();
- continue;
- }
- return comp == 0;
- }
- return false;
- }
-
- public void onMergeCancelled() {
- mIsMergeCancelled = true;
- }
-
- /**
- * Carry out a merge of the given diffs, and add the results to
- * the given MergeResult. If we are the first merge to find
- * client-side diffs, we'll use the given ContentProvider to
- * construct a temporary instance to hold them.
- */
- public void merge(final SyncContext context,
- final Account account,
- final SyncableContentProvider serverDiffs,
- TempProviderSyncResult result,
- SyncResult syncResult, SyncableContentProvider temporaryInstanceFactory) {
- mIsMergeCancelled = false;
- if (serverDiffs != null) {
- if (!mDb.isDbLockedByCurrentThread()) {
- throw new IllegalStateException("this must be called from within a DB transaction");
- }
- mergeServerDiffs(context, account, serverDiffs, syncResult);
- notifyChanges();
- }
-
- if (result != null) {
- findLocalChanges(result, temporaryInstanceFactory, account, syncResult);
- }
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "merge complete");
- }
-
- /**
- * @hide this is public for testing purposes only
- */
- public void mergeServerDiffs(SyncContext context,
- Account account, SyncableContentProvider serverDiffs, SyncResult syncResult) {
- boolean diffsArePartial = serverDiffs.getContainsDiffs();
- // mark the current rows so that we can distinguish these from new
- // inserts that occur during the merge
- mDb.update(mTable, mSyncMarkValues, null, null);
- if (mDeletedTable != null) {
- mDb.update(mDeletedTable, mSyncMarkValues, null, null);
- }
-
- Cursor localCursor = null;
- Cursor deletedCursor = null;
- Cursor diffsCursor = null;
- try {
- // load the local database entries, so we can merge them with the server
- final String[] accountSelectionArgs = new String[]{account.name, account.type};
- localCursor = mDb.query(mTable, syncDirtyProjection,
- SELECT_MARKED, accountSelectionArgs, null, null,
- mTable + "." + _SYNC_ID);
- if (mDeletedTable != null) {
- deletedCursor = mDb.query(mDeletedTable, syncIdAndVersionProjection,
- SELECT_MARKED, accountSelectionArgs, null, null,
- mDeletedTable + "." + _SYNC_ID);
- } else {
- deletedCursor =
- mDb.rawQuery("select 'a' as _sync_id, 'b' as _sync_version limit 0", null);
- }
-
- // Apply updates and insertions from the server
- diffsCursor = serverDiffs.query(mTableURL,
- null, null, null, mTable + "." + _SYNC_ID);
- int deletedSyncIDColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_ID);
- int deletedSyncVersionColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_VERSION);
- int serverSyncIDColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_ID);
- int serverSyncVersionColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_VERSION);
- int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
-
- String lastSyncId = null;
- int diffsCount = 0;
- int localCount = 0;
- localCursor.moveToFirst();
- deletedCursor.moveToFirst();
- while (diffsCursor.moveToNext()) {
- if (mIsMergeCancelled) {
- return;
- }
- mDb.yieldIfContended();
- String serverSyncId = diffsCursor.getString(serverSyncIDColumn);
- String serverSyncVersion = diffsCursor.getString(serverSyncVersionColumn);
- long localRowId = 0;
- String localSyncVersion = null;
-
- diffsCount++;
- context.setStatusText("Processing " + diffsCount + "/"
- + diffsCursor.getCount());
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "processing server entry " +
- diffsCount + ", " + serverSyncId);
-
- if (TRACE) {
- if (diffsCount == 10) {
- Debug.startMethodTracing("atmtrace");
- }
- if (diffsCount == 20) {
- Debug.stopMethodTracing();
- }
- }
-
- boolean conflict = false;
- boolean update = false;
- boolean insert = false;
-
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "found event with serverSyncID " + serverSyncId);
- }
- if (TextUtils.isEmpty(serverSyncId)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.e(TAG, "server entry doesn't have a serverSyncID");
- }
- continue;
- }
-
- // It is possible that the sync adapter wrote the same record multiple times,
- // e.g. if the same record came via multiple feeds. If this happens just ignore
- // the duplicate records.
- if (serverSyncId.equals(lastSyncId)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "skipping record with duplicate remote server id " + lastSyncId);
- }
- continue;
- }
- lastSyncId = serverSyncId;
-
- String localSyncID = null;
- boolean localSyncDirty = false;
-
- while (!localCursor.isAfterLast()) {
- if (mIsMergeCancelled) {
- return;
- }
- localCount++;
- localSyncID = localCursor.getString(2);
-
- // If the local record doesn't have a _sync_id then
- // it is new. Ignore it for now, we will send an insert
- // the the server later.
- if (TextUtils.isEmpty(localSyncID)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "local record " +
- localCursor.getLong(1) +
- " has no _sync_id, ignoring");
- }
- localCursor.moveToNext();
- localSyncID = null;
- continue;
- }
-
- int comp = serverSyncId.compareTo(localSyncID);
-
- // the local DB has a record that the server doesn't have
- if (comp > 0) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "local record " +
- localCursor.getLong(1) +
- " has _sync_id " + localSyncID +
- " that is < server _sync_id " + serverSyncId);
- }
- if (diffsArePartial) {
- localCursor.moveToNext();
- } else {
- deleteRow(localCursor);
- if (mDeletedTable != null) {
- mDb.delete(mDeletedTable, _SYNC_ID +"=?", new String[] {localSyncID});
- }
- syncResult.stats.numDeletes++;
- mDb.yieldIfContended();
- }
- localSyncID = null;
- continue;
- }
-
- // the server has a record that the local DB doesn't have
- if (comp < 0) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "local record " +
- localCursor.getLong(1) +
- " has _sync_id " + localSyncID +
- " that is > server _sync_id " + serverSyncId);
- }
- localSyncID = null;
- }
-
- // the server and the local DB both have this record
- if (comp == 0) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "local record " +
- localCursor.getLong(1) +
- " has _sync_id " + localSyncID +
- " that matches the server _sync_id");
- }
- localSyncDirty = localCursor.getInt(0) != 0;
- localRowId = localCursor.getLong(1);
- localSyncVersion = localCursor.getString(3);
- localCursor.moveToNext();
- }
-
- break;
- }
-
- // If this record is in the deleted table then update the server version
- // in the deleted table, if necessary, and then ignore it here.
- // We will send a deletion indication to the server down a
- // little further.
- if (findInCursor(deletedCursor, deletedSyncIDColumn, serverSyncId)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "remote record " + serverSyncId + " is in the deleted table");
- }
- final String deletedSyncVersion = deletedCursor.getString(deletedSyncVersionColumn);
- if (!TextUtils.equals(deletedSyncVersion, serverSyncVersion)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "setting version of deleted record " + serverSyncId + " to "
- + serverSyncVersion);
- }
- ContentValues values = new ContentValues();
- values.put(_SYNC_VERSION, serverSyncVersion);
- mDb.update(mDeletedTable, values, "_sync_id=?", new String[]{serverSyncId});
- }
- continue;
- }
-
- // If the _sync_local_id is present in the diffsCursor
- // then this record corresponds to a local record that was just
- // inserted into the server and the _sync_local_id is the row id
- // of the local record. Set these fields so that the next check
- // treats this record as an update, which will allow the
- // merger to update the record with the server's sync id
- if (!diffsCursor.isNull(serverSyncLocalIdColumn)) {
- localRowId = diffsCursor.getLong(serverSyncLocalIdColumn);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "the remote record with sync id " + serverSyncId
- + " has a local sync id, " + localRowId);
- }
- localSyncID = serverSyncId;
- localSyncDirty = false;
- localSyncVersion = null;
- }
-
- if (!TextUtils.isEmpty(localSyncID)) {
- // An existing server item has changed
- // If serverSyncVersion is null, there is no edit URL;
- // server won't let this change be written.
- boolean recordChanged = (localSyncVersion == null) ||
- (serverSyncVersion == null) ||
- !serverSyncVersion.equals(localSyncVersion);
- if (recordChanged) {
- if (localSyncDirty) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "remote record " + serverSyncId
- + " conflicts with local _sync_id " + localSyncID
- + ", local _id " + localRowId);
- }
- conflict = true;
- } else {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG,
- "remote record " +
- serverSyncId +
- " updates local _sync_id " +
- localSyncID + ", local _id " +
- localRowId);
- }
- update = true;
- }
- } else {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG,
- "Skipping update: localSyncVersion: " + localSyncVersion +
- ", serverSyncVersion: " + serverSyncVersion);
- }
- }
- } else {
- // the local db doesn't know about this record so add it
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "remote record " + serverSyncId + " is new, inserting");
- }
- insert = true;
- }
-
- if (update) {
- updateRow(localRowId, serverDiffs, diffsCursor);
- syncResult.stats.numUpdates++;
- } else if (conflict) {
- resolveRow(localRowId, serverSyncId, serverDiffs, diffsCursor);
- syncResult.stats.numUpdates++;
- } else if (insert) {
- insertRow(serverDiffs, diffsCursor);
- syncResult.stats.numInserts++;
- }
- }
-
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "processed " + diffsCount + " server entries");
- }
-
- // If tombstones aren't in use delete any remaining local rows that
- // don't have corresponding server rows. Keep the rows that don't
- // have a sync id since those were created locally and haven't been
- // synced to the server yet.
- if (!diffsArePartial) {
- while (!localCursor.isAfterLast() && !TextUtils.isEmpty(localCursor.getString(2))) {
- if (mIsMergeCancelled) {
- return;
- }
- localCount++;
- final String localSyncId = localCursor.getString(2);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG,
- "deleting local record " +
- localCursor.getLong(1) +
- " _sync_id " + localSyncId);
- }
- deleteRow(localCursor);
- if (mDeletedTable != null) {
- mDb.delete(mDeletedTable, _SYNC_ID + "=?", new String[] {localSyncId});
- }
- syncResult.stats.numDeletes++;
- mDb.yieldIfContended();
- }
- }
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "checked " + localCount +
- " local entries");
- } finally {
- if (diffsCursor != null) diffsCursor.close();
- if (localCursor != null) localCursor.close();
- if (deletedCursor != null) deletedCursor.close();
- }
-
-
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "applying deletions from the server");
-
- // Apply deletions from the server
- if (mDeletedTableURL != null) {
- diffsCursor = serverDiffs.query(mDeletedTableURL, null, null, null, null);
- try {
- while (diffsCursor.moveToNext()) {
- if (mIsMergeCancelled) {
- return;
- }
- // delete all rows that match each element in the diffsCursor
- fullyDeleteMatchingRows(diffsCursor, account, syncResult);
- mDb.yieldIfContended();
- }
- } finally {
- diffsCursor.close();
- }
- }
- }
-
- private void fullyDeleteMatchingRows(Cursor diffsCursor, Account account,
- SyncResult syncResult) {
- int serverSyncIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_ID);
- final boolean deleteBySyncId = !diffsCursor.isNull(serverSyncIdColumn);
-
- // delete the rows explicitly so that the delete operation can be overridden
- final String[] selectionArgs;
- Cursor c = null;
- try {
- if (deleteBySyncId) {
- selectionArgs = new String[]{diffsCursor.getString(serverSyncIdColumn),
- account.name, account.type};
- c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_SYNC_ID_AND_ACCOUNT,
- selectionArgs, null, null, null);
- } else {
- int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
- selectionArgs = new String[]{diffsCursor.getString(serverSyncLocalIdColumn)};
- c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_ID, selectionArgs,
- null, null, null);
- }
- c.moveToFirst();
- while (!c.isAfterLast()) {
- deleteRow(c); // advances the cursor
- syncResult.stats.numDeletes++;
- }
- } finally {
- if (c != null) c.close();
- }
- if (deleteBySyncId && mDeletedTable != null) {
- mDb.delete(mDeletedTable, SELECT_BY_SYNC_ID_AND_ACCOUNT, selectionArgs);
- }
- }
-
- /**
- * Converts cursor into a Map, using the correct types for the values.
- */
- protected void cursorRowToContentValues(Cursor cursor, ContentValues map) {
- DatabaseUtils.cursorRowToContentValues(cursor, map);
- }
-
- /**
- * Finds local changes, placing the results in the given result object.
- * @param temporaryInstanceFactory As an optimization for the case
- * where there are no client-side diffs, mergeResult may initially
- * have no {@link TempProviderSyncResult#tempContentProvider}. If this is
- * the first in the sequence of AbstractTableMergers to find
- * client-side diffs, it will use the given ContentProvider to
- * create a temporary instance and store its {@link
- * android.content.ContentProvider} in the mergeResult.
- * @param account
- * @param syncResult
- */
- private void findLocalChanges(TempProviderSyncResult mergeResult,
- SyncableContentProvider temporaryInstanceFactory, Account account,
- SyncResult syncResult) {
- SyncableContentProvider clientDiffs = mergeResult.tempContentProvider;
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "generating client updates");
-
- final String[] accountSelectionArgs = new String[]{account.name, account.type};
-
- // Generate the client updates and insertions
- // Create a cursor for dirty records
- long numInsertsOrUpdates = 0;
- Cursor localChangesCursor = mDb.query(mTable, null, SELECT_UNSYNCED, accountSelectionArgs,
- null, null, null);
- try {
- numInsertsOrUpdates = localChangesCursor.getCount();
- while (localChangesCursor.moveToNext()) {
- if (mIsMergeCancelled) {
- return;
- }
- if (clientDiffs == null) {
- clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
- }
- mValues.clear();
- cursorRowToContentValues(localChangesCursor, mValues);
- mValues.remove("_id");
- DatabaseUtils.cursorLongToContentValues(localChangesCursor, "_id", mValues,
- _SYNC_LOCAL_ID);
- clientDiffs.insert(mTableURL, mValues);
- }
- } finally {
- localChangesCursor.close();
- }
-
- // Generate the client deletions
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "generating client deletions");
- long numEntries = DatabaseUtils.queryNumEntries(mDb, mTable);
- long numDeletedEntries = 0;
- if (mDeletedTable != null) {
- Cursor deletedCursor = mDb.query(mDeletedTable,
- syncIdAndVersionProjection,
- _SYNC_ACCOUNT + "=? AND " + _SYNC_ACCOUNT_TYPE + "=? AND "
- + _SYNC_ID + " IS NOT NULL", accountSelectionArgs,
- null, null, mDeletedTable + "." + _SYNC_ID);
- try {
- numDeletedEntries = deletedCursor.getCount();
- while (deletedCursor.moveToNext()) {
- if (mIsMergeCancelled) {
- return;
- }
- if (clientDiffs == null) {
- clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
- }
- mValues.clear();
- DatabaseUtils.cursorRowToContentValues(deletedCursor, mValues);
- clientDiffs.insert(mDeletedTableURL, mValues);
- }
- } finally {
- deletedCursor.close();
- }
- }
-
- if (clientDiffs != null) {
- mergeResult.tempContentProvider = clientDiffs;
- }
- syncResult.stats.numDeletes += numDeletedEntries;
- syncResult.stats.numUpdates += numInsertsOrUpdates;
- syncResult.stats.numEntries += numEntries;
- }
-}
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index 154c4a6..14bc5dd 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -117,7 +117,7 @@
if (mSyncThread != null
&& mSyncThread.mSyncContext.getSyncContextBinder()
== syncContext.asBinder()) {
- mSyncThread.interrupt();
+ onSyncCanceled(mSyncThread);
}
}
}
@@ -207,4 +207,15 @@
*/
public abstract void onPerformSync(Account account, Bundle extras,
String authority, ContentProviderClient provider, SyncResult syncResult);
+
+ /**
+ * Indicates that a sync operation has been canceled. This will be invoked on a separate
+ * thread than the sync thread and so you must consider the multi-threaded implications
+ * of the work that you do in this method.
+ *
+ * @param thread the thread that is running the sync operation to cancel
+ */
+ public void onSyncCanceled(Thread thread) {
+ thread.interrupt();
+ }
}
diff --git a/core/java/android/content/SyncAdapter.java b/core/java/android/content/SyncAdapter.java
deleted file mode 100644
index a5afe87..0000000
--- a/core/java/android/content/SyncAdapter.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2006 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.content;
-
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.accounts.Account;
-
-/**
- * @hide
- */
-public abstract class SyncAdapter {
- private static final String TAG = "SyncAdapter";
-
- /** Kernel event log tag. */
- public static final int LOG_SYNC_DETAILS = EventLogTags.SYNC_DETAILS;
-
- class Transport extends ISyncAdapter.Stub {
- public void startSync(ISyncContext syncContext, String authority, Account account,
- Bundle extras) throws RemoteException {
- SyncAdapter.this.startSync(new SyncContext(syncContext), account, authority, extras);
- }
-
- public void cancelSync(ISyncContext syncContext) throws RemoteException {
- SyncAdapter.this.cancelSync();
- }
-
- public void initialize(Account account, String authority) throws RemoteException {
- Bundle extras = new Bundle();
- extras.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
- startSync(null, authority, account, extras);
- }
- }
-
- Transport mTransport = new Transport();
-
- /**
- * Get the Transport object.
- */
- public final ISyncAdapter getISyncAdapter()
- {
- return mTransport;
- }
-
- /**
- * Initiate a sync for this account. SyncAdapter-specific parameters may
- * be specified in extras, which is guaranteed to not be null. IPC invocations
- * of this method and cancelSync() are guaranteed to be serialized.
- *
- * @param syncContext the ISyncContext used to indicate the progress of the sync. When
- * the sync is finished (successfully or not) ISyncContext.onFinished() must be called.
- * @param account the account that should be synced
- * @param authority the authority if the sync request
- * @param extras SyncAdapter-specific parameters
- */
- public abstract void startSync(SyncContext syncContext, Account account, String authority,
- Bundle extras);
-
- /**
- * Cancel the most recently initiated sync. Due to race conditions, this may arrive
- * after the ISyncContext.onFinished() for that sync was called. IPC invocations
- * of this method and startSync() are guaranteed to be serialized.
- */
- public abstract void cancelSync();
-}
diff --git a/core/java/android/content/SyncStateContentProviderHelper.java b/core/java/android/content/SyncStateContentProviderHelper.java
deleted file mode 100644
index 64bbe25..0000000
--- a/core/java/android/content/SyncStateContentProviderHelper.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import com.android.internal.util.ArrayUtils;
-
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.net.Uri;
-import android.accounts.Account;
-
-/**
- * Extends the schema of a ContentProvider to include the _sync_state table
- * and implements query/insert/update/delete to access that table using the
- * authority "syncstate". This can be used to store the sync state for a
- * set of accounts.
- *
- * @hide
- */
-public class SyncStateContentProviderHelper {
- final SQLiteOpenHelper mOpenHelper;
-
- private static final String SYNC_STATE_AUTHORITY = "syncstate";
- private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
- private static final int STATE = 0;
-
- private static final Uri CONTENT_URI =
- Uri.parse("content://" + SYNC_STATE_AUTHORITY + "/state");
-
- private static final String ACCOUNT_WHERE = "_sync_account = ? AND _sync_account_type = ?";
-
- private final Provider mInternalProviderInterface;
-
- private static final String SYNC_STATE_TABLE = "_sync_state";
- private static long DB_VERSION = 3;
-
- private static final String[] ACCOUNT_PROJECTION =
- new String[]{"_sync_account", "_sync_account_type"};
-
- static {
- sURIMatcher.addURI(SYNC_STATE_AUTHORITY, "state", STATE);
- }
-
- public SyncStateContentProviderHelper(SQLiteOpenHelper openHelper) {
- mOpenHelper = openHelper;
- mInternalProviderInterface = new Provider();
- }
-
- public ContentProvider asContentProvider() {
- return mInternalProviderInterface;
- }
-
- public void createDatabase(SQLiteDatabase db) {
- db.execSQL("DROP TABLE IF EXISTS _sync_state");
- db.execSQL("CREATE TABLE _sync_state (" +
- "_id INTEGER PRIMARY KEY," +
- "_sync_account TEXT," +
- "_sync_account_type TEXT," +
- "data TEXT," +
- "UNIQUE(_sync_account, _sync_account_type)" +
- ");");
-
- db.execSQL("DROP TABLE IF EXISTS _sync_state_metadata");
- db.execSQL("CREATE TABLE _sync_state_metadata (" +
- "version INTEGER" +
- ");");
- ContentValues values = new ContentValues();
- values.put("version", DB_VERSION);
- db.insert("_sync_state_metadata", "version", values);
- }
-
- protected void onDatabaseOpened(SQLiteDatabase db) {
- long version = DatabaseUtils.longForQuery(db,
- "select version from _sync_state_metadata", null);
- if (version != DB_VERSION) {
- createDatabase(db);
- }
- }
-
- class Provider extends ContentProvider {
- public boolean onCreate() {
- throw new UnsupportedOperationException("not implemented");
- }
-
- public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- SQLiteDatabase db = mOpenHelper.getReadableDatabase();
- int match = sURIMatcher.match(url);
- switch (match) {
- case STATE:
- return db.query(SYNC_STATE_TABLE, projection, selection, selectionArgs,
- null, null, sortOrder);
- default:
- throw new UnsupportedOperationException("Cannot query URL: " + url);
- }
- }
-
- public String getType(Uri uri) {
- throw new UnsupportedOperationException("not implemented");
- }
-
- public Uri insert(Uri url, ContentValues values) {
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- int match = sURIMatcher.match(url);
- switch (match) {
- case STATE: {
- long id = db.insert(SYNC_STATE_TABLE, "feed", values);
- return CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build();
- }
- default:
- throw new UnsupportedOperationException("Cannot insert into URL: " + url);
- }
- }
-
- public int delete(Uri url, String userWhere, String[] whereArgs) {
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- switch (sURIMatcher.match(url)) {
- case STATE:
- return db.delete(SYNC_STATE_TABLE, userWhere, whereArgs);
- default:
- throw new IllegalArgumentException("Unknown URL " + url);
- }
-
- }
-
- public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) {
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- switch (sURIMatcher.match(url)) {
- case STATE:
- return db.update(SYNC_STATE_TABLE, values, selection, selectionArgs);
- default:
- throw new UnsupportedOperationException("Cannot update URL: " + url);
- }
-
- }
- }
-
- /**
- * Check if the url matches content that this ContentProvider manages.
- * @param url the Uri to check
- * @return true if this ContentProvider can handle that Uri.
- */
- public boolean matches(Uri url) {
- return (SYNC_STATE_AUTHORITY.equals(url.getAuthority()));
- }
-
- /**
- * Replaces the contents of the _sync_state table in the destination ContentProvider
- * with the row that matches account, if any, in the source ContentProvider.
- * <p>
- * The ContentProviders must expose the _sync_state table as URI content://syncstate/state.
- * @param dbSrc the database to read from
- * @param dbDest the database to write to
- * @param account the account of the row that should be copied over.
- */
- public void copySyncState(SQLiteDatabase dbSrc, SQLiteDatabase dbDest,
- Account account) {
- final String[] whereArgs = new String[]{account.name, account.type};
- Cursor c = dbSrc.query(SYNC_STATE_TABLE,
- new String[]{"_sync_account", "_sync_account_type", "data"},
- ACCOUNT_WHERE, whereArgs, null, null, null);
- try {
- if (c.moveToNext()) {
- ContentValues values = new ContentValues();
- values.put("_sync_account", c.getString(0));
- values.put("_sync_account_type", c.getString(1));
- values.put("data", c.getBlob(2));
- dbDest.replace(SYNC_STATE_TABLE, "_sync_account", values);
- }
- } finally {
- c.close();
- }
- }
-
- public void onAccountsChanged(Account[] accounts) {
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
- try {
- while (c.moveToNext()) {
- final String accountName = c.getString(0);
- final String accountType = c.getString(1);
- Account account = new Account(accountName, accountType);
- if (!ArrayUtils.contains(accounts, account)) {
- db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE,
- new String[]{accountName, accountType});
- }
- }
- } finally {
- c.close();
- }
- }
-
- public void discardSyncData(SQLiteDatabase db, Account account) {
- if (account != null) {
- db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account.name, account.type});
- } else {
- db.delete(SYNC_STATE_TABLE, null, null);
- }
- }
-
- /**
- * Retrieves the SyncData bytes for the given account. The byte array returned may be null.
- */
- public byte[] readSyncDataBytes(SQLiteDatabase db, Account account) {
- Cursor c = db.query(SYNC_STATE_TABLE, null, ACCOUNT_WHERE,
- new String[]{account.name, account.type}, null, null, null);
- try {
- if (c.moveToFirst()) {
- return c.getBlob(c.getColumnIndexOrThrow("data"));
- }
- } finally {
- c.close();
- }
- return null;
- }
-
- /**
- * Sets the SyncData bytes for the given account. The bytes array may be null.
- */
- public void writeSyncDataBytes(SQLiteDatabase db, Account account, byte[] data) {
- ContentValues values = new ContentValues();
- values.put("data", data);
- db.update(SYNC_STATE_TABLE, values, ACCOUNT_WHERE,
- new String[]{account.name, account.type});
- }
-}
diff --git a/core/java/android/content/SyncableContentProvider.java b/core/java/android/content/SyncableContentProvider.java
deleted file mode 100644
index ab4e91c..0000000
--- a/core/java/android/content/SyncableContentProvider.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.accounts.Account;
-
-import java.util.Map;
-
-/**
- * A specialization of the ContentProvider that centralizes functionality
- * used by ContentProviders that are syncable. It also wraps calls to the ContentProvider
- * inside of database transactions.
- *
- * @hide
- */
-public abstract class SyncableContentProvider extends ContentProvider {
- protected abstract boolean isTemporary();
-
- private volatile TempProviderSyncAdapter mTempProviderSyncAdapter;
-
- public void setTempProviderSyncAdapter(TempProviderSyncAdapter syncAdapter) {
- mTempProviderSyncAdapter = syncAdapter;
- }
-
- public TempProviderSyncAdapter getTempProviderSyncAdapter() {
- return mTempProviderSyncAdapter;
- }
-
- /**
- * Close resources that must be closed. You must call this to properly release
- * the resources used by the SyncableContentProvider.
- */
- public abstract void close();
-
- /**
- * Override to create your schema and do anything else you need to do with a new database.
- * This is run inside a transaction (so you don't need to use one).
- * This method may not use getDatabase(), or call content provider methods, it must only
- * use the database handle passed to it.
- */
- protected abstract void bootstrapDatabase(SQLiteDatabase db);
-
- /**
- * Override to upgrade your database from an old version to the version you specified.
- * Don't set the DB version, this will automatically be done after the method returns.
- * This method may not use getDatabase(), or call content provider methods, it must only
- * use the database handle passed to it.
- *
- * @param oldVersion version of the existing database
- * @param newVersion current version to upgrade to
- * @return true if the upgrade was lossless, false if it was lossy
- */
- protected abstract boolean upgradeDatabase(SQLiteDatabase db, int oldVersion, int newVersion);
-
- /**
- * Override to do anything (like cleanups or checks) you need to do after opening a database.
- * Does nothing by default. This is run inside a transaction (so you don't need to use one).
- * This method may not use getDatabase(), or call content provider methods, it must only
- * use the database handle passed to it.
- */
- protected abstract void onDatabaseOpened(SQLiteDatabase db);
-
- /**
- * Get a non-persistent instance of this content provider.
- * You must call {@link #close} on the returned
- * SyncableContentProvider when you are done with it.
- *
- * @return a non-persistent content provider with the same layout as this
- * provider.
- */
- public abstract SyncableContentProvider getTemporaryInstance();
-
- public abstract SQLiteDatabase getDatabase();
-
- public abstract boolean getContainsDiffs();
-
- public abstract void setContainsDiffs(boolean containsDiffs);
-
- /**
- * Each subclass of this class should define a subclass of {@link
- * AbstractTableMerger} for each table they wish to merge. It
- * should then override this method and return one instance of
- * each merger, in sequence. Their {@link
- * AbstractTableMerger#merge merge} methods will be called, one at a
- * time, in the order supplied.
- *
- * <p>The default implementation returns an empty list, so that no
- * merging will occur.
- * @return A sequence of subclasses of {@link
- * AbstractTableMerger}, one for each table that should be merged.
- */
- protected abstract Iterable<? extends AbstractTableMerger> getMergers();
-
- /**
- * Check if changes to this URI can be syncable changes.
- * @param uri the URI of the resource that was changed
- * @return true if changes to this URI can be syncable changes, false otherwise
- */
- public abstract boolean changeRequiresLocalSync(Uri uri);
-
- /**
- * Called right before a sync is started.
- *
- * @param context the sync context for the operation
- * @param account
- */
- public abstract void onSyncStart(SyncContext context, Account account);
-
- /**
- * Called right after a sync is completed
- *
- * @param context the sync context for the operation
- * @param success true if the sync succeeded, false if an error occurred
- */
- public abstract void onSyncStop(SyncContext context, boolean success);
-
- /**
- * The account of the most recent call to onSyncStart()
- * @return the account
- */
- public abstract Account getSyncingAccount();
-
- /**
- * Merge diffs from a sync source with this content provider.
- *
- * @param context the SyncContext within which this merge is taking place
- * @param diffs A temporary content provider containing diffs from a sync
- * source.
- * @param result a MergeResult that contains information about the merge, including
- * a temporary content provider with the same layout as this provider containing
- * @param syncResult
- */
- public abstract void merge(SyncContext context, SyncableContentProvider diffs,
- TempProviderSyncResult result, SyncResult syncResult);
-
-
- /**
- * Invoked when the active sync has been canceled. The default
- * implementation doesn't do anything (except ensure that this
- * provider is syncable). Subclasses of ContentProvider
- * that support canceling of sync should override this.
- */
- public abstract void onSyncCanceled();
-
-
- public abstract boolean isMergeCancelled();
-
- /**
- * Subclasses should override this instead of update(). See update()
- * for details.
- *
- * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
- * which means a database transaction will be active during the call;
- */
- protected abstract int updateInternal(Uri url, ContentValues values,
- String selection, String[] selectionArgs);
-
- /**
- * Subclasses should override this instead of delete(). See delete()
- * for details.
- *
- * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
- * which means a database transaction will be active during the call;
- */
- protected abstract int deleteInternal(Uri url, String selection, String[] selectionArgs);
-
- /**
- * Subclasses should override this instead of insert(). See insert()
- * for details.
- *
- * <p> This method is called within a acquireDbLock()/releaseDbLock() block,
- * which means a database transaction will be active during the call;
- */
- protected abstract Uri insertInternal(Uri url, ContentValues values);
-
- /**
- * Subclasses should override this instead of query(). See query()
- * for details.
- *
- * <p> This method is *not* called within a acquireDbLock()/releaseDbLock()
- * block for performance reasons. If an implementation needs atomic access
- * to the database the lock can be acquired then.
- */
- protected abstract Cursor queryInternal(Uri url, String[] projection,
- String selection, String[] selectionArgs, String sortOrder);
-
- /**
- * Make sure that there are no entries for accounts that no longer exist
- * @param accountsArray the array of currently-existing accounts
- */
- protected abstract void onAccountsChanged(Account[] accountsArray);
-
- /**
- * A helper method to delete all rows whose account is not in the accounts
- * map. The accountColumnName is the name of the column that is expected
- * to hold the account. If a row has an empty account it is never deleted.
- *
- * @param accounts a map of existing accounts
- * @param table the table to delete from
- */
- protected abstract void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts,
- String table);
-
- /**
- * Called when the sync system determines that this provider should no longer
- * contain records for the specified account.
- */
- public abstract void wipeAccount(Account account);
-
- /**
- * Retrieves the SyncData bytes for the given account. The byte array returned may be null.
- */
- public abstract byte[] readSyncDataBytes(Account account);
-
- /**
- * Sets the SyncData bytes for the given account. The bytes array may be null.
- */
- public abstract void writeSyncDataBytes(Account account, byte[] data);
-}
-
diff --git a/core/java/android/content/TempProviderSyncAdapter.java b/core/java/android/content/TempProviderSyncAdapter.java
deleted file mode 100644
index 5ccaa26..0000000
--- a/core/java/android/content/TempProviderSyncAdapter.java
+++ /dev/null
@@ -1,585 +0,0 @@
-package android.content;
-
-import android.accounts.Account;
-import android.accounts.AuthenticatorException;
-import android.accounts.OperationCanceledException;
-import android.database.SQLException;
-import android.net.TrafficStats;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.Parcelable;
-import android.os.Process;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.util.Config;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.TimingLogger;
-
-import java.io.IOException;
-
-/**
- * @hide
- */
-public abstract class TempProviderSyncAdapter extends SyncAdapter {
- private static final String TAG = "Sync";
-
- private static final int MAX_GET_SERVER_DIFFS_LOOP_COUNT = 20;
- private static final int MAX_UPLOAD_CHANGES_LOOP_COUNT = 10;
- private static final int NUM_ALLOWED_SIMULTANEOUS_DELETIONS = 5;
- private static final long PERCENT_ALLOWED_SIMULTANEOUS_DELETIONS = 20;
-
- private volatile SyncableContentProvider mProvider;
- private volatile SyncThread mSyncThread = null;
- private volatile boolean mProviderSyncStarted;
- private volatile boolean mAdapterSyncStarted;
-
- public TempProviderSyncAdapter(SyncableContentProvider provider) {
- super();
- mProvider = provider;
- }
-
- /**
- * Used by getServerDiffs() to track the sync progress for a given
- * sync adapter. Implementations of SyncAdapter generally specialize
- * this class in order to track specific data about that SyncAdapter's
- * sync. If an implementation of SyncAdapter doesn't need to store
- * any data for a sync it may use TrivialSyncData.
- */
- public static abstract class SyncData implements Parcelable {
-
- }
-
- public final void setContext(Context context) {
- mContext = context;
- }
-
- /**
- * Retrieve the Context this adapter is running in. Only available
- * once onSyncStarting() is called (not available from constructor).
- */
- final public Context getContext() {
- return mContext;
- }
-
- /**
- * Called right before a sync is started.
- *
- * @param context allows you to publish status and interact with the
- * @param account the account to sync
- * @param manualSync true if this sync was requested manually by the user
- * @param result information to track what happened during this sync attempt
- */
- public abstract void onSyncStarting(SyncContext context, Account account, boolean manualSync,
- SyncResult result);
-
- /**
- * Called right after a sync is completed
- *
- * @param context allows you to publish status and interact with the
- * user during interactive syncs.
- * @param success true if the sync suceeded, false if an error occured
- */
- public abstract void onSyncEnding(SyncContext context, boolean success);
-
- /**
- * Implement this to return true if the data in your content provider
- * is read only.
- */
- public abstract boolean isReadOnly();
-
- public abstract boolean getIsSyncable(Account account)
- throws IOException, AuthenticatorException, OperationCanceledException;
-
- /**
- * Get diffs from the server since the last completed sync and put them
- * into a temporary provider.
- *
- * @param context allows you to publish status and interact with the
- * user during interactive syncs.
- * @param syncData used to track the progress this client has made in syncing data
- * from the server
- * @param tempProvider this is where the diffs should be stored
- * @param extras any extra data describing the sync that is desired
- * @param syncInfo sync adapter-specific data that is used during a single sync operation
- * @param syncResult information to track what happened during this sync attempt
- */
- public abstract void getServerDiffs(SyncContext context,
- SyncData syncData, SyncableContentProvider tempProvider,
- Bundle extras, Object syncInfo, SyncResult syncResult);
-
- /**
- * Send client diffs to the server, optionally receiving more diffs from the server
- *
- * @param context allows you to publish status and interact with the
- * user during interactive syncs.
- * @param clientDiffs the diffs from the client
- * @param serverDiffs the SyncableContentProvider that should be populated with
-* the entries that were returned in response to an insert/update/delete request
-* to the server
- * @param syncResult information to track what happened during this sync attempt
- * @param dontActuallySendDeletes
- */
- public abstract void sendClientDiffs(SyncContext context,
- SyncableContentProvider clientDiffs,
- SyncableContentProvider serverDiffs, SyncResult syncResult,
- boolean dontActuallySendDeletes);
-
- /**
- * Reads the sync data from the ContentProvider
- * @param contentProvider the ContentProvider to read from
- * @return the SyncData for the provider. This may be null.
- */
- public SyncData readSyncData(SyncableContentProvider contentProvider) {
- return null;
- }
-
- /**
- * Create and return a new, empty SyncData object
- */
- public SyncData newSyncData() {
- return null;
- }
-
- /**
- * Stores the sync data in the Sync Stats database, keying it by
- * the account that was set in the last call to onSyncStarting()
- */
- public void writeSyncData(SyncData syncData, SyncableContentProvider contentProvider) {}
-
- /**
- * Indicate to the SyncAdapter that the last sync that was started has
- * been cancelled.
- */
- public abstract void onSyncCanceled();
-
- /**
- * Initializes the temporary content providers used during
- * {@link TempProviderSyncAdapter#sendClientDiffs}.
- * May copy relevant data from the underlying db into this provider so
- * joins, etc., can work.
- *
- * @param cp The ContentProvider to initialize.
- */
- protected void initTempProvider(SyncableContentProvider cp) {}
-
- protected Object createSyncInfo() {
- return null;
- }
-
- /**
- * Called when the accounts list possibly changed, to give the
- * SyncAdapter a chance to do any necessary bookkeeping, e.g.
- * to make sure that any required SubscribedFeeds subscriptions
- * exist.
- * @param accounts the list of accounts
- */
- public abstract void onAccountsChanged(Account[] accounts);
-
- private Context mContext;
-
- private class SyncThread extends Thread {
- private final Account mAccount;
- private final String mAuthority;
- private final Bundle mExtras;
- private final SyncContext mSyncContext;
- private volatile boolean mIsCanceled = false;
- private long mInitialTxBytes;
- private long mInitialRxBytes;
- private final SyncResult mResult;
-
- SyncThread(SyncContext syncContext, Account account, String authority, Bundle extras) {
- super("SyncThread");
- mAccount = account;
- mAuthority = authority;
- mExtras = extras;
- mSyncContext = syncContext;
- mResult = new SyncResult();
- }
-
- void cancelSync() {
- mIsCanceled = true;
- if (mAdapterSyncStarted) onSyncCanceled();
- if (mProviderSyncStarted) mProvider.onSyncCanceled();
- // We may lose the last few sync events when canceling. Oh well.
- int uid = Process.myUid();
- logSyncDetails(TrafficStats.getUidTxBytes(uid) - mInitialTxBytes,
- TrafficStats.getUidRxBytes(uid) - mInitialRxBytes, mResult);
- }
-
- @Override
- public void run() {
- Process.setThreadPriority(Process.myTid(),
- Process.THREAD_PRIORITY_BACKGROUND);
- int uid = Process.myUid();
- mInitialTxBytes = TrafficStats.getUidTxBytes(uid);
- mInitialRxBytes = TrafficStats.getUidRxBytes(uid);
- try {
- sync(mSyncContext, mAccount, mAuthority, mExtras);
- } catch (SQLException e) {
- Log.e(TAG, "Sync failed", e);
- mResult.databaseError = true;
- } finally {
- mSyncThread = null;
- if (!mIsCanceled) {
- logSyncDetails(TrafficStats.getUidTxBytes(uid) - mInitialTxBytes,
- TrafficStats.getUidRxBytes(uid) - mInitialRxBytes, mResult);
- mSyncContext.onFinished(mResult);
- }
- }
- }
-
- private void sync(SyncContext syncContext, Account account, String authority,
- Bundle extras) {
- mIsCanceled = false;
-
- mProviderSyncStarted = false;
- mAdapterSyncStarted = false;
- String message = null;
-
- // always attempt to initialize if the isSyncable state isn't set yet
- int isSyncable = ContentResolver.getIsSyncable(account, authority);
- if (isSyncable < 0) {
- try {
- isSyncable = (getIsSyncable(account)) ? 1 : 0;
- ContentResolver.setIsSyncable(account, authority, isSyncable);
- } catch (IOException e) {
- ++mResult.stats.numIoExceptions;
- } catch (AuthenticatorException e) {
- ++mResult.stats.numParseExceptions;
- } catch (OperationCanceledException e) {
- // do nothing
- }
- }
-
- // if this is an initialization request then our work is done here
- if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
- return;
- }
-
- // if we aren't syncable then get out
- if (isSyncable <= 0) {
- return;
- }
-
- boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
-
- try {
- mProvider.onSyncStart(syncContext, account);
- mProviderSyncStarted = true;
- onSyncStarting(syncContext, account, manualSync, mResult);
- if (mResult.hasError()) {
- message = "SyncAdapter failed while trying to start sync";
- return;
- }
- mAdapterSyncStarted = true;
- if (mIsCanceled) {
- return;
- }
- final String syncTracingEnabledValue = SystemProperties.get(TAG + "Tracing");
- final boolean syncTracingEnabled = !TextUtils.isEmpty(syncTracingEnabledValue);
- try {
- if (syncTracingEnabled) {
- System.gc();
- System.gc();
- Debug.startMethodTracing("synctrace." + System.currentTimeMillis());
- }
- runSyncLoop(syncContext, account, extras);
- } finally {
- if (syncTracingEnabled) Debug.stopMethodTracing();
- }
- onSyncEnding(syncContext, !mResult.hasError());
- mAdapterSyncStarted = false;
- mProvider.onSyncStop(syncContext, true);
- mProviderSyncStarted = false;
- } finally {
- if (mAdapterSyncStarted) {
- mAdapterSyncStarted = false;
- onSyncEnding(syncContext, false);
- }
- if (mProviderSyncStarted) {
- mProviderSyncStarted = false;
- mProvider.onSyncStop(syncContext, false);
- }
- if (!mIsCanceled) {
- if (message != null) syncContext.setStatusText(message);
- }
- }
- }
-
- private void runSyncLoop(SyncContext syncContext, Account account, Bundle extras) {
- TimingLogger syncTimer = new TimingLogger(TAG + "Profiling", "sync");
- syncTimer.addSplit("start");
- int loopCount = 0;
- boolean tooManyGetServerDiffsAttempts = false;
-
- final boolean overrideTooManyDeletions =
- extras.getBoolean(ContentResolver.SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS,
- false);
- final boolean discardLocalDeletions =
- extras.getBoolean(ContentResolver.SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS, false);
- boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD,
- false /* default this flag to false */);
- SyncableContentProvider serverDiffs = null;
- TempProviderSyncResult result = new TempProviderSyncResult();
- try {
- if (!uploadOnly) {
- /**
- * This loop repeatedly calls SyncAdapter.getServerDiffs()
- * (to get changes from the feed) followed by
- * ContentProvider.merge() (to incorporate these changes
- * into the provider), stopping when the SyncData returned
- * from getServerDiffs() indicates that all the data was
- * fetched.
- */
- while (!mIsCanceled) {
- // Don't let a bad sync go forever
- if (loopCount++ == MAX_GET_SERVER_DIFFS_LOOP_COUNT) {
- Log.e(TAG, "runSyncLoop: Hit max loop count while getting server diffs "
- + getClass().getName());
- // TODO: change the structure here to schedule a new sync
- // with a backoff time, keeping track to be sure
- // we don't keep doing this forever (due to some bug or
- // mismatch between the client and the server)
- tooManyGetServerDiffsAttempts = true;
- break;
- }
-
- // Get an empty content provider to put the diffs into
- if (serverDiffs != null) serverDiffs.close();
- serverDiffs = mProvider.getTemporaryInstance();
-
- // Get records from the server which will be put into the serverDiffs
- initTempProvider(serverDiffs);
- Object syncInfo = createSyncInfo();
- SyncData syncData = readSyncData(serverDiffs);
- // syncData will only be null if there was a demarshalling error
- // while reading the sync data.
- if (syncData == null) {
- mProvider.wipeAccount(account);
- syncData = newSyncData();
- }
- mResult.clear();
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "runSyncLoop: running getServerDiffs using syncData "
- + syncData.toString());
- }
- getServerDiffs(syncContext, syncData, serverDiffs, extras, syncInfo,
- mResult);
-
- if (mIsCanceled) return;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "runSyncLoop: result: " + mResult);
- }
- if (mResult.hasError()) return;
- if (mResult.partialSyncUnavailable) {
- if (Config.LOGD) {
- Log.d(TAG, "partialSyncUnavailable is set, setting "
- + "ignoreSyncData and retrying");
- }
- mProvider.wipeAccount(account);
- continue;
- }
-
- // write the updated syncData back into the temp provider
- writeSyncData(syncData, serverDiffs);
-
- // apply the downloaded changes to the provider
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "runSyncLoop: running merge");
- }
- mProvider.merge(syncContext, serverDiffs,
- null /* don't return client diffs */, mResult);
- if (mIsCanceled) return;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "runSyncLoop: result: " + mResult);
- }
-
- // if the server has no more changes then break out of the loop
- if (!mResult.moreRecordsToGet) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "runSyncLoop: fetched all data, moving on");
- }
- break;
- }
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "runSyncLoop: more data to fetch, looping");
- }
- }
- }
-
- /**
- * This loop repeatedly calls ContentProvider.merge() followed
- * by SyncAdapter.merge() until either indicate that there is
- * no more work to do by returning null.
- * <p>
- * The initial ContentProvider.merge() returns a temporary
- * ContentProvider that contains any local changes that need
- * to be committed to the server.
- * <p>
- * The SyncAdapter.merge() calls upload the changes to the server
- * and populates temporary provider (the serverDiffs) with the
- * result.
- * <p>
- * Subsequent calls to ContentProvider.merge() incoporate the
- * result of previous SyncAdapter.merge() calls into the
- * real ContentProvider and again return a temporary
- * ContentProvider that contains any local changes that need
- * to be committed to the server.
- */
- loopCount = 0;
- boolean readOnly = isReadOnly();
- long previousNumModifications = 0;
- if (serverDiffs != null) {
- serverDiffs.close();
- serverDiffs = null;
- }
-
- // If we are discarding local deletions then we need to redownload all the items
- // again (since some of them might have been deleted). We do this by deleting the
- // sync data for the current account by writing in a null one.
- if (discardLocalDeletions) {
- serverDiffs = mProvider.getTemporaryInstance();
- initTempProvider(serverDiffs);
- writeSyncData(null, serverDiffs);
- }
-
- while (!mIsCanceled) {
- if (Config.LOGV) {
- Log.v(TAG, "runSyncLoop: Merging diffs from server to client");
- }
- if (result.tempContentProvider != null) {
- result.tempContentProvider.close();
- result.tempContentProvider = null;
- }
- mResult.clear();
- mProvider.merge(syncContext, serverDiffs, readOnly ? null : result,
- mResult);
- if (mIsCanceled) return;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "runSyncLoop: result: " + mResult);
- }
-
- SyncableContentProvider clientDiffs =
- readOnly ? null : result.tempContentProvider;
- if (clientDiffs == null) {
- // Nothing to commit back to the server
- if (Config.LOGV) Log.v(TAG, "runSyncLoop: No client diffs");
- break;
- }
-
- long numModifications = mResult.stats.numUpdates
- + mResult.stats.numDeletes
- + mResult.stats.numInserts;
-
- // as long as we are making progress keep resetting the loop count
- if (numModifications < previousNumModifications) {
- loopCount = 0;
- }
- previousNumModifications = numModifications;
-
- // Don't let a bad sync go forever
- if (loopCount++ >= MAX_UPLOAD_CHANGES_LOOP_COUNT) {
- Log.e(TAG, "runSyncLoop: Hit max loop count while syncing "
- + getClass().getName());
- mResult.tooManyRetries = true;
- break;
- }
-
- if (!overrideTooManyDeletions && !discardLocalDeletions
- && hasTooManyDeletions(mResult.stats)) {
- if (Config.LOGD) {
- Log.d(TAG, "runSyncLoop: Too many deletions were found in provider "
- + getClass().getName() + ", not doing any more updates");
- }
- long numDeletes = mResult.stats.numDeletes;
- mResult.stats.clear();
- mResult.tooManyDeletions = true;
- mResult.stats.numDeletes = numDeletes;
- break;
- }
-
- if (Config.LOGV) Log.v(TAG, "runSyncLoop: Merging diffs from client to server");
- if (serverDiffs != null) serverDiffs.close();
- serverDiffs = clientDiffs.getTemporaryInstance();
- initTempProvider(serverDiffs);
- mResult.clear();
- sendClientDiffs(syncContext, clientDiffs, serverDiffs, mResult,
- discardLocalDeletions);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "runSyncLoop: result: " + mResult);
- }
-
- if (!mResult.madeSomeProgress()) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "runSyncLoop: No data from client diffs merge");
- }
- break;
- }
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "runSyncLoop: made some progress, looping");
- }
- }
-
- // add in any status codes that we saved from earlier
- mResult.tooManyRetries |= tooManyGetServerDiffsAttempts;
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "runSyncLoop: final result: " + mResult);
- }
- } finally {
- // do this in the finally block to guarantee that is is set and not overwritten
- if (discardLocalDeletions) {
- mResult.fullSyncRequested = true;
- }
- if (serverDiffs != null) serverDiffs.close();
- if (result.tempContentProvider != null) result.tempContentProvider.close();
- syncTimer.addSplit("stop");
- syncTimer.dumpToLog();
- }
- }
- }
-
- /**
- * Logs details on the sync.
- * Normally this will be overridden by a subclass that will provide
- * provider-specific details.
- *
- * @param bytesSent number of bytes the sync sent over the network
- * @param bytesReceived number of bytes the sync received over the network
- * @param result The SyncResult object holding info on the sync
- */
- protected void logSyncDetails(long bytesSent, long bytesReceived, SyncResult result) {
- EventLog.writeEvent(SyncAdapter.LOG_SYNC_DETAILS, TAG, bytesSent, bytesReceived, "");
- }
-
- public void startSync(SyncContext syncContext, Account account, String authority,
- Bundle extras) {
- if (mSyncThread != null) {
- syncContext.onFinished(SyncResult.ALREADY_IN_PROGRESS);
- return;
- }
-
- mSyncThread = new SyncThread(syncContext, account, authority, extras);
- mSyncThread.start();
- }
-
- public void cancelSync() {
- if (mSyncThread != null) {
- mSyncThread.cancelSync();
- }
- }
-
- protected boolean hasTooManyDeletions(SyncStats stats) {
- long numEntries = stats.numEntries;
- long numDeletedEntries = stats.numDeletes;
-
- long percentDeleted = (numDeletedEntries == 0)
- ? 0
- : (100 * numDeletedEntries /
- (numEntries + numDeletedEntries));
- boolean tooManyDeletions =
- (numDeletedEntries > NUM_ALLOWED_SIMULTANEOUS_DELETIONS)
- && (percentDeleted > PERCENT_ALLOWED_SIMULTANEOUS_DELETIONS);
- return tooManyDeletions;
- }
-}
diff --git a/core/java/android/content/TempProviderSyncResult.java b/core/java/android/content/TempProviderSyncResult.java
deleted file mode 100644
index 81f6f79..0000000
--- a/core/java/android/content/TempProviderSyncResult.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-/**
- * Used to hold data returned from a given phase of a TempProviderSync.
- * @hide
- */
-public class TempProviderSyncResult {
- /**
- * An interface to a temporary content provider that contains
- * the result of updates that were sent to the server. This
- * provider must be merged into the permanent content provider.
- * This may be null, which indicates that there is nothing to
- * merge back into the content provider.
- */
- public SyncableContentProvider tempContentProvider;
-
- public TempProviderSyncResult() {
- tempContentProvider = null;
- }
-}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 117de15..e98d8ee 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -30,6 +30,7 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteException;
import android.graphics.Rect;
import android.net.Uri;
import android.os.RemoteException;
@@ -375,6 +376,124 @@
}
/**
+ * Constants for various styles of combining given name, family name etc into
+ * a full name. For example, the western tradition follows the pattern
+ * 'given name' 'middle name' 'family name' with the alternative pattern being
+ * 'family name', 'given name' 'middle name'. The CJK tradition is
+ * 'family name' 'middle name' 'given name', with Japanese favoring a space between
+ * the names and Chinese omitting the space.
+ * @hide
+ */
+ public interface FullNameStyle {
+ public static final int UNDEFINED = 0;
+ public static final int WESTERN = 1;
+
+ /**
+ * Used if the name is written in Hanzi/Kanji/Hanja and we could not determine
+ * which specific language it belongs to: Chinese, Japanese or Korean.
+ */
+ public static final int CJK = 2;
+
+ public static final int CHINESE = 3;
+ public static final int JAPANESE = 4;
+ public static final int KOREAN = 5;
+ }
+
+ /**
+ * Constants for various styles of capturing the pronunciation of a person's name.
+ * @hide
+ */
+ public interface PhoneticNameStyle {
+ public static final int UNDEFINED = 0;
+
+ /**
+ * Pinyin is a phonetic method of entering Chinese characters. Typically not explicitly
+ * shown in UIs, but used for searches and sorting.
+ */
+ public static final int PINYIN = 3;
+
+ /**
+ * Hiragana and Katakana are two common styles of writing out the pronunciation
+ * of a Japanese names.
+ */
+ public static final int JAPANESE = 4;
+
+ /**
+ * Hangul is the Korean phonetic alphabet.
+ */
+ public static final int KOREAN = 5;
+ }
+
+ /**
+ * Types of data used to produce the display name for a contact. Listed in the order
+ * of increasing priority.
+ *
+ * @hide
+ */
+ public interface DisplayNameSources {
+ public static final int UNDEFINED = 0;
+ public static final int EMAIL = 10;
+ public static final int PHONE = 20;
+ public static final int ORGANIZATION = 30;
+ public static final int NICKNAME = 35;
+ public static final int STRUCTURED_NAME = 40;
+ }
+
+ /**
+ * @see Contacts
+ * @see RawContacts
+ * @hide
+ */
+ protected interface ContactNameColumns {
+
+ /**
+ * The kind of data that is used as the display name for the contact, see
+ * DisplayNameSources.
+ */
+ public static final String DISPLAY_NAME_SOURCE = "display_name_source";
+
+ /**
+ * The default text shown as the contact's display name. It is based on
+ * available data, see {@link #DISPLAY_NAME_SOURCE}.
+ */
+ public static final String DISPLAY_NAME = "display_name";
+
+ /**
+ * Alternative representation of the display name. If display name is
+ * based on the structured name and the structured name follows
+ * the Western full name style, then this field contains the "family name first"
+ * version of the full name. Otherwise, it is the same as {@link #DISPLAY_NAME}.
+ */
+ public static final String DISPLAY_NAME_ALTERNATIVE = "display_name_alt";
+
+ /**
+ * The type of alphabet used to capture the phonetic name. See
+ * PhoneticNameStyle.
+ */
+ public static final String PHONETIC_NAME_STYLE = "phonetic_name_style";
+
+ /**
+ * Pronunciation of the full name. See PhoneticNameStyle.
+ */
+ public static final String PHONETIC_NAME = "phonetic_name";
+
+ /**
+ * Sort key that takes into account locale-based traditions for sorting
+ * names in address books. More specifically, for Chinese names
+ * the sort key is the name's Pinyin spelling; for Japanese names
+ * it is the Hiragana version of the phonetic name.
+ */
+ public static final String SORT_KEY = "sort_key";
+
+ /**
+ * Sort key based on the alternative representation of the full name,
+ * specifically the one using the 'family name first' format for
+ * Western names.
+ */
+ public static final String SORT_KEY_ALTERNATIVE = "sort_key_alt";
+ }
+
+ /**
* Constants for the contacts table, which contains a record per aggregate
* of raw contacts representing the same person.
* <h3>Operations</h3>
@@ -1233,7 +1352,7 @@
* </table>
*/
public static final class RawContacts implements BaseColumns, RawContactsColumns,
- ContactOptionsColumns, SyncColumns {
+ ContactOptionsColumns, ContactNameColumns, SyncColumns {
/**
* This utility class cannot be instantiated
*/
@@ -1447,7 +1566,11 @@
if (cursor.isNull(columnIndex)) {
// don't put anything
} else {
- cv.put(key, cursor.getString(columnIndex));
+ try {
+ cv.put(key, cursor.getString(columnIndex));
+ } catch (SQLiteException e) {
+ cv.put(key, cursor.getBlob(columnIndex));
+ }
}
// TODO: go back to this version of the code when bug
// http://b/issue?id=2306370 is fixed.
@@ -2884,6 +3007,21 @@
* <P>Type: TEXT</P>
*/
public static final String PHONETIC_FAMILY_NAME = DATA9;
+
+ /**
+ * The style used for combining given/middle/family name into a full name.
+ * See {@link ContactsContract.FullNameStyle}.
+ *
+ * @hide
+ */
+ public static final String FULL_NAME_STYLE = DATA10;
+
+ /**
+ * The alphabet used for capturing the phonetic name.
+ * See {@link ContactsContract.PhoneticNameStyle}.
+ * @hide
+ */
+ public static final String PHONETIC_NAME_STYLE = DATA11;
}
/**
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index f71af20..b36fa8d 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -782,10 +782,13 @@
return mSettings.getUserAgentString();
}
- // these ids need to be in sync with enum RAW_RES_ID in WebFrame
+ // These ids need to be in sync with enum rawResId in PlatformBridge.h
private static final int NODOMAIN = 1;
private static final int LOADERROR = 2;
private static final int DRAWABLEDIR = 3;
+ private static final int FILE_UPLOAD_LABEL = 4;
+ private static final int RESET_LABEL = 5;
+ private static final int SUBMIT_LABEL = 6;
String getRawResFilename(int id) {
int resid;
@@ -803,6 +806,18 @@
resid = com.android.internal.R.drawable.btn_check_off;
break;
+ case FILE_UPLOAD_LABEL:
+ return mContext.getResources().getString(
+ com.android.internal.R.string.upload_file);
+
+ case RESET_LABEL:
+ return mContext.getResources().getString(
+ com.android.internal.R.string.reset);
+
+ case SUBMIT_LABEL:
+ return mContext.getResources().getString(
+ com.android.internal.R.string.submit);
+
default:
Log.e(LOGTAG, "getRawResFilename got incompatible resource ID");
return "";
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 201cc0c..455b593 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4531,6 +4531,11 @@
// Now use the delta to determine the actual amount of text
// we need.
partialEndOffset += delta;
+ if (partialStartOffset > N) {
+ partialStartOffset = N;
+ } else if (partialStartOffset < 0) {
+ partialStartOffset = 0;
+ }
if (partialEndOffset > N) {
partialEndOffset = N;
} else if (partialEndOffset < 0) {
diff --git a/core/java/com/google/android/net/GoogleHttpClient.java b/core/java/com/google/android/net/GoogleHttpClient.java
deleted file mode 100644
index 0337672..0000000
--- a/core/java/com/google/android/net/GoogleHttpClient.java
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.android.net;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.net.TrafficStats;
-import android.os.Build;
-import android.os.SystemClock;
-import android.provider.Checkin;
-import android.util.Config;
-import android.util.Log;
-import com.android.common.AndroidHttpClient;
-import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpEntityEnclosingRequest;
-import org.apache.http.HttpHost;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.ProtocolException;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.ResponseHandler;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.conn.ClientConnectionManager;
-import org.apache.http.conn.scheme.LayeredSocketFactory;
-import org.apache.http.conn.scheme.Scheme;
-import org.apache.http.conn.scheme.SchemeRegistry;
-import org.apache.http.conn.scheme.SocketFactory;
-import org.apache.http.impl.client.EntityEnclosingRequestWrapper;
-import org.apache.http.impl.client.RequestWrapper;
-import org.apache.http.params.HttpParams;
-import org.apache.http.protocol.HttpContext;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.URI;
-import java.net.URISyntaxException;
-
-/**
- * {@link AndroidHttpClient} wrapper that uses {@link UrlRules} to rewrite URLs
- * and otherwise tweak HTTP requests.
- */
-public class GoogleHttpClient implements HttpClient {
- private static final String TAG = "GoogleHttpClient";
- private static final boolean LOCAL_LOGV = Config.LOGV || false;
-
- /** Exception thrown when a request is blocked by the URL rules. */
- public static class BlockedRequestException extends IOException {
- private final UrlRules.Rule mRule;
- BlockedRequestException(UrlRules.Rule rule) {
- super("Blocked by rule: " + rule.mName);
- mRule = rule;
- }
- }
-
- private final AndroidHttpClient mClient;
- private final ContentResolver mResolver;
- private final String mAppName, mUserAgent;
- private final ThreadLocal<Boolean> mConnectionAllocated = new ThreadLocal<Boolean>();
-
- /**
- * Create an HTTP client without SSL session persistence.
- * @deprecated Use {@link #GoogleHttpClient(android.content.Context, String, boolean)}
- */
- public GoogleHttpClient(ContentResolver resolver, String userAgent) {
- mClient = AndroidHttpClient.newInstance(userAgent);
- mResolver = resolver;
- mUserAgent = mAppName = userAgent;
- }
-
- /**
- * Create an HTTP client without SSL session persistence.
- * @deprecated Use {@link #GoogleHttpClient(android.content.Context, String, boolean)}
- */
- public GoogleHttpClient(ContentResolver resolver, String appAndVersion,
- boolean gzipCapable) {
- this(resolver, null /* cache */, appAndVersion, gzipCapable);
- }
-
- /**
- * Create an HTTP client. Normaly this client is shared throughout an app.
- * The HTTP client will construct its User-Agent as follows:
- *
- * <appAndVersion> (<build device> <build id>)
- * or
- * <appAndVersion> (<build device> <build id>); gzip
- * (if gzip capable)
- *
- * The context has settings for URL rewriting rules and is used to enable
- * SSL session persistence.
- *
- * @param context application context.
- * @param appAndVersion Base app and version to use in the User-Agent.
- * e.g., "MyApp/1.0"
- * @param gzipCapable Whether or not this client is able to consume gzip'd
- * responses. Only used to modify the User-Agent, not other request
- * headers. Needed because Google servers require gzip in the User-Agent
- * in order to return gzip'd content.
- */
- public GoogleHttpClient(Context context, String appAndVersion, boolean gzipCapable) {
- this(context.getContentResolver(),
- SSLClientSessionCacheFactory.getCache(context),
- appAndVersion, gzipCapable);
- }
-
- private GoogleHttpClient(ContentResolver resolver,
- SSLClientSessionCache cache,
- String appAndVersion, boolean gzipCapable) {
- String userAgent = appAndVersion + " (" + Build.DEVICE + " " + Build.ID + ")";
- if (gzipCapable) {
- userAgent = userAgent + "; gzip";
- }
-
- mClient = AndroidHttpClient.newInstance(userAgent, cache);
- mResolver = resolver;
- mAppName = appAndVersion;
- mUserAgent = userAgent;
-
- // Wrap all the socket factories with the appropriate wrapper. (Apache
- // HTTP, curse its black and stupid heart, inspects the SocketFactory to
- // see if it's a LayeredSocketFactory, so we need two wrapper classes.)
- SchemeRegistry registry = getConnectionManager().getSchemeRegistry();
- for (String name : registry.getSchemeNames()) {
- Scheme scheme = registry.unregister(name);
- SocketFactory sf = scheme.getSocketFactory();
- if (sf instanceof LayeredSocketFactory) {
- sf = new WrappedLayeredSocketFactory((LayeredSocketFactory) sf);
- } else {
- sf = new WrappedSocketFactory(sf);
- }
- registry.register(new Scheme(name, sf, scheme.getDefaultPort()));
- }
- }
-
- /**
- * Delegating wrapper for SocketFactory records when sockets are connected.
- * We use this to know whether a connection was created vs reused, to
- * gather per-app statistics about connection reuse rates.
- * (Note, we record only *connection*, not *creation* of sockets --
- * what we care about is the network overhead of an actual TCP connect.)
- */
- private class WrappedSocketFactory implements SocketFactory {
- private SocketFactory mDelegate;
- private WrappedSocketFactory(SocketFactory delegate) { mDelegate = delegate; }
- public final Socket createSocket() throws IOException { return mDelegate.createSocket(); }
- public final boolean isSecure(Socket s) { return mDelegate.isSecure(s); }
-
- public final Socket connectSocket(
- Socket s, String h, int p,
- InetAddress la, int lp, HttpParams params) throws IOException {
- mConnectionAllocated.set(Boolean.TRUE);
- return mDelegate.connectSocket(s, h, p, la, lp, params);
- }
- }
-
- /** Like WrappedSocketFactory, but for the LayeredSocketFactory subclass. */
- private class WrappedLayeredSocketFactory
- extends WrappedSocketFactory implements LayeredSocketFactory {
- private LayeredSocketFactory mDelegate;
- private WrappedLayeredSocketFactory(LayeredSocketFactory sf) { super(sf); mDelegate = sf; }
-
- public final Socket createSocket(Socket s, String host, int port, boolean autoClose)
- throws IOException {
- return mDelegate.createSocket(s, host, port, autoClose);
- }
- }
-
- /**
- * Release resources associated with this client. You must call this,
- * or significant resources (sockets and memory) may be leaked.
- */
- public void close() {
- mClient.close();
- }
-
- /** Execute a request without applying and rewrite rules. */
- public HttpResponse executeWithoutRewriting(
- HttpUriRequest request, HttpContext context)
- throws IOException {
- int code = -1;
- long start = SystemClock.elapsedRealtime();
- try {
- HttpResponse response;
- mConnectionAllocated.set(null);
-
- if (NetworkStatsEntity.shouldLogNetworkStats()) {
- // TODO: if we're logging network stats, and if the apache library is configured
- // to follow redirects, count each redirect as an additional round trip.
-
- int uid = android.os.Process.myUid();
- long startTx = TrafficStats.getUidTxBytes(uid);
- long startRx = TrafficStats.getUidRxBytes(uid);
-
- response = mClient.execute(request, context);
- HttpEntity origEntity = response == null ? null : response.getEntity();
- if (origEntity != null) {
- // yeah, we compute the same thing below. we do need to compute this here
- // so we can wrap the HttpEntity in the response.
- long now = SystemClock.elapsedRealtime();
- long elapsed = now - start;
- NetworkStatsEntity entity = new NetworkStatsEntity(origEntity,
- mAppName, uid, startTx, startRx,
- elapsed /* response latency */, now /* processing start time */);
- response.setEntity(entity);
- }
- } else {
- response = mClient.execute(request, context);
- }
-
- code = response.getStatusLine().getStatusCode();
- return response;
- } finally {
- // Record some statistics to the checkin service about the outcome.
- // Note that this is only describing execute(), not body download.
- // We assume the database writes are much faster than network I/O,
- // and not worth running in a background thread or anything.
- try {
- long elapsed = SystemClock.elapsedRealtime() - start;
- ContentValues values = new ContentValues();
- values.put(Checkin.Stats.COUNT, 1);
- values.put(Checkin.Stats.SUM, elapsed / 1000.0);
-
- values.put(Checkin.Stats.TAG, Checkin.Stats.Tag.HTTP_REQUEST + ":" + mAppName);
- mResolver.insert(Checkin.Stats.CONTENT_URI, values);
-
- // No sockets and no exceptions means we successfully reused a connection
- if (mConnectionAllocated.get() == null && code >= 0) {
- values.put(Checkin.Stats.TAG, Checkin.Stats.Tag.HTTP_REUSED + ":" + mAppName);
- mResolver.insert(Checkin.Stats.CONTENT_URI, values);
- }
-
- String status = code < 0 ? "IOException" : Integer.toString(code);
- values.put(Checkin.Stats.TAG,
- Checkin.Stats.Tag.HTTP_STATUS + ":" + mAppName + ":" + status);
- mResolver.insert(Checkin.Stats.CONTENT_URI, values);
- } catch (Exception e) {
- Log.e(TAG, "Error recording stats", e);
- }
- }
- }
-
- public String rewriteURI(String original) {
- UrlRules rules = UrlRules.getRules(mResolver);
- UrlRules.Rule rule = rules.matchRule(original);
- return rule.apply(original);
- }
-
- public HttpResponse execute(HttpUriRequest request, HttpContext context)
- throws IOException {
- // Rewrite the supplied URL...
- URI uri = request.getURI();
- String original = uri.toString();
- UrlRules rules = UrlRules.getRules(mResolver);
- UrlRules.Rule rule = rules.matchRule(original);
- String rewritten = rule.apply(original);
-
- if (rewritten == null) {
- Log.w(TAG, "Blocked by " + rule.mName + ": " + original);
- throw new BlockedRequestException(rule);
- } else if (rewritten == original) {
- return executeWithoutRewriting(request, context); // Pass through
- }
-
- try {
- uri = new URI(rewritten);
- } catch (URISyntaxException e) {
- throw new RuntimeException("Bad URL from rule: " + rule.mName, e);
- }
-
- // Wrap request so we can replace the URI.
- RequestWrapper wrapper = wrapRequest(request);
- wrapper.setURI(uri);
- request = wrapper;
-
- if (LOCAL_LOGV) Log.v(TAG, "Rule " + rule.mName + ": " + original + " -> " + rewritten);
- return executeWithoutRewriting(request, context);
- }
-
- /**
- * Wraps the request making it mutable.
- */
- private static RequestWrapper wrapRequest(HttpUriRequest request)
- throws IOException {
- try {
- // We have to wrap it with the right type. Some code performs
- // instanceof checks.
- RequestWrapper wrapped;
- if (request instanceof HttpEntityEnclosingRequest) {
- wrapped = new EntityEnclosingRequestWrapper(
- (HttpEntityEnclosingRequest) request);
- } else {
- wrapped = new RequestWrapper(request);
- }
-
- // Copy the headers from the original request into the wrapper.
- wrapped.resetHeaders();
-
- return wrapped;
- } catch (ProtocolException e) {
- throw new ClientProtocolException(e);
- }
- }
-
- /**
- * Mark a user agent as one Google will trust to handle gzipped content.
- * {@link AndroidHttpClient#modifyRequestToAcceptGzipResponse} is (also)
- * necessary but not sufficient -- many browsers claim to accept gzip but
- * have broken handling, so Google checks the user agent as well.
- *
- * @param originalUserAgent to modify (however you identify yourself)
- * @return user agent with a "yes, I really can handle gzip" token added.
- * @deprecated Use {@link #GoogleHttpClient(android.content.ContentResolver, String, boolean)}
- */
- public static String getGzipCapableUserAgent(String originalUserAgent) {
- return originalUserAgent + "; gzip";
- }
-
- // HttpClient wrapper methods.
-
- public HttpParams getParams() {
- return mClient.getParams();
- }
-
- public ClientConnectionManager getConnectionManager() {
- return mClient.getConnectionManager();
- }
-
- public HttpResponse execute(HttpUriRequest request) throws IOException {
- return execute(request, (HttpContext) null);
- }
-
- public HttpResponse execute(HttpHost target, HttpRequest request)
- throws IOException {
- return mClient.execute(target, request);
- }
-
- public HttpResponse execute(HttpHost target, HttpRequest request,
- HttpContext context) throws IOException {
- return mClient.execute(target, request, context);
- }
-
- public <T> T execute(HttpUriRequest request,
- ResponseHandler<? extends T> responseHandler)
- throws IOException, ClientProtocolException {
- return mClient.execute(request, responseHandler);
- }
-
- public <T> T execute(HttpUriRequest request,
- ResponseHandler<? extends T> responseHandler, HttpContext context)
- throws IOException, ClientProtocolException {
- return mClient.execute(request, responseHandler, context);
- }
-
- public <T> T execute(HttpHost target, HttpRequest request,
- ResponseHandler<? extends T> responseHandler) throws IOException,
- ClientProtocolException {
- return mClient.execute(target, request, responseHandler);
- }
-
- public <T> T execute(HttpHost target, HttpRequest request,
- ResponseHandler<? extends T> responseHandler, HttpContext context)
- throws IOException, ClientProtocolException {
- return mClient.execute(target, request, responseHandler, context);
- }
-
- /**
- * Enables cURL request logging for this client.
- *
- * @param name to log messages with
- * @param level at which to log messages (see {@link android.util.Log})
- */
- public void enableCurlLogging(String name, int level) {
- mClient.enableCurlLogging(name, level);
- }
-
- /**
- * Disables cURL logging for this client.
- */
- public void disableCurlLogging() {
- mClient.disableCurlLogging();
- }
-}
diff --git a/core/res/res/values-en-rUS/donottranslate-names.xml b/core/res/res/values-en-rUS/donottranslate-names.xml
index ae38ddf..f8ec765 100644
--- a/core/res/res/values-en-rUS/donottranslate-names.xml
+++ b/core/res/res/values-en-rUS/donottranslate-names.xml
@@ -156,8 +156,8 @@
MRS, MS, PASTOR, PROF, REP, REVEREND, REV, SEN, ST
</string>
<string name="common_name_suffixes">
- B.A., BA, D.D.S., DDS, I, II, III, IV, IX, JR, M.A., M.D, MA,
- MD, MS, PH.D., PHD, SR, V, VI, VII, VIII, X
+ B.A., BA, D.D.S., DDS, I, II, III, IV, IX, JR., M.A., M.D., MA,
+ MD, MS, PH.D., PHD, SR., V, VI, VII, VIII, X
</string>
<string name="common_last_name_prefixes">
D\', DE, DEL, DI, LA, LE, MC, SAN, ST, TER, VAN, VON
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9e72f64..bc354c5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2097,6 +2097,12 @@
<string name="l2tp_vpn_description">Layer 2 Tunneling Protocol</string>
<string name="l2tp_ipsec_psk_vpn_description">Pre-shared key based L2TP/IPSec VPN</string>
<string name="l2tp_ipsec_crt_vpn_description">Certificate based L2TP/IPSec VPN</string>
+
+ <!-- Localized strings for WebView -->
<!-- Label for button in a WebView that will open a chooser to choose a file to upload -->
<string name="upload_file">Choose file</string>
+ <!-- Label for <input type="reset"> button in html -->
+ <string name="reset">Reset</string>
+ <!-- Label for <input type="submit"> button in html -->
+ <string name="submit">Submit</string>
</resources>
diff --git a/docs/html/guide/basics/what-is-android.jd b/docs/html/guide/basics/what-is-android.jd
index 668e62e..9393fab 100644
--- a/docs/html/guide/basics/what-is-android.jd
+++ b/docs/html/guide/basics/what-is-android.jd
@@ -64,7 +64,7 @@
<p>Underlying all applications is a set of services and systems, including:
<ul>
<li>A rich and extensible set of <a
- href="{@docRoot}guide/tutorials/views/index.html">Views</a> that can be used to
+ href="{@docRoot}resources/tutorials/views/index.html">Views</a> that can be used to
build an application, including lists, grids, text boxes, buttons, and even
an embeddable web browser</li>
<li><a href="{@docRoot}guide/topics/providers/content-providers.html">Content
@@ -80,7 +80,7 @@
</ul>
<p>For more details and a walkthrough of an application, see the <a
-href="{@docRoot}guide/tutorials/notepad/index.html">Notepad Tutorial</a>.</p>
+href="{@docRoot}resources/tutorials/notepad/index.html">Notepad Tutorial</a>.</p>
<a name="libraries" id="libraries"></a>
<h2>Libraries</h2>
diff --git a/docs/html/guide/developing/debug-tasks.jd b/docs/html/guide/developing/debug-tasks.jd
index 6b7c27a..3279741 100644
--- a/docs/html/guide/developing/debug-tasks.jd
+++ b/docs/html/guide/developing/debug-tasks.jd
@@ -60,7 +60,7 @@
that expose useful information such as CPU usage and frame rate. See <a href="#additionaldebugging">Debug
and Test Settings on the Emulator</a> below. </li>
</ul>
-<p>Also, see the <a href="{@docRoot}guide/appendix/faq/troubleshooting.html">Troubleshooting</a> section
+<p>Also, see the <a href="{@docRoot}resources/faq/troubleshooting.html">Troubleshooting</a> section
of the doc to figure out why your application isn't appearing on the emulator,
or why it's not starting. </p>
diff --git a/docs/html/guide/developing/tools/adt.jd b/docs/html/guide/developing/tools/adt.jd
index 219be67..eb7ad4f 100644
--- a/docs/html/guide/developing/tools/adt.jd
+++ b/docs/html/guide/developing/tools/adt.jd
@@ -105,7 +105,7 @@
installing ADT, your Eclipse installation might not include those components.
For information about how to quickly add the necessary components to your
Eclipse installation, see the troubleshooting topic
-<a href="{@docRoot}guide/appendix/faq/troubleshooting.html#installeclipsecomponents">ADT Installation Error: "requires plug-in org.eclipse.wst.sse.ui"</a>.</p>
+<a href="{@docRoot}resources/faq/troubleshooting.html#installeclipsecomponents">ADT Installation Error: "requires plug-in org.eclipse.wst.sse.ui"</a>.</p>
<h4>For Linux users</h4>
<p>If you encounter this error when installing the ADT Plugin for Eclipse:
diff --git a/docs/html/guide/developing/tools/emulator.jd b/docs/html/guide/developing/tools/emulator.jd
index 43f9271..507353e 100644
--- a/docs/html/guide/developing/tools/emulator.jd
+++ b/docs/html/guide/developing/tools/emulator.jd
@@ -1754,7 +1754,7 @@
<p>For emulators running on Mac OS X, if you see an error "Warning: No DNS servers found" when starting the emulator, check to see whether you have an <code>/etc/resolv.conf</code> file. If not, please run the following line in a command window:</p>
<pre>ln -s /private/var/run/resolv.conf /etc/resolv.conf</pre>
-<p>See <a href="{@docRoot}guide/appendix/faq/index.html">Frequently Asked Questions</a> for more troubleshooting information. </p>
+<p>See <a href="{@docRoot}resources/faq/index.html">Frequently Asked Questions</a> for more troubleshooting information. </p>
<a name="limitations"></a>
<h2>Emulator Limitations</h2>
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index c99d61c..13d752a 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -363,81 +363,6 @@
</a></li>
</ul>
</li>
-
- <li>
- <h2><span class="en">Tutorials and Sample Code</span>
- <span class="de" style="display:none">Lernprogramme und Beispielcode</span>
- <span class="es" style="display:none">Tutoriales y código de ejemplo</span>
- <span class="fr" style="display:none">Didacticiels et exemple de code</span>
- <span class="it" style="display:none">Esercitazioni e codice di esempio</span>
- <span class="ja" style="display:none">チュートリアルとサンプル コード</span>
- <span class="zh-CN" style="display:none">辅导手册和示例代码</span>
- <span class="zh-TW" style="display:none">教學課程與程式碼範例</span>
- </h2>
- <ul>
- <li><a href="<?cs var:toroot ?>guide/tutorials/hello-world.html">
- <span class="en">Hello World</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/tutorials/views/index.html">
- <span class="en">Hello Views</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/tutorials/localization/index.html">
- <span class="en">Hello Localization</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/tutorials/notepad/index.html">
- <span class="en">Notepad Tutorial</span>
- </a></li>
- </ul>
- <ul>
- <li class="toggle-list">
- <div><a href="<?cs var:toroot ?>guide/samples/index.html">
- <span class="en">Sample Code</span>
- </a></div>
- <ul>
- <li><a href="<?cs var:toroot ?>guide/samples/ApiDemos/index.html">
- <span class="en">API Demos</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/BluetoothChat/index.html">
- <span class="en">Bluetooth Chat</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/ContactManager/index.html">
- <span class="en">Contact Manager</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/Home/index.html">
- <span class="en">Home</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/JetBoy/index.html">
- <span class="en">JetBoy</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/LunarLander/index.html">
- <span class="en">Lunar Lander</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/MultiResolution/index.html">
- <span class="en">Multiple Resolutions</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/NotePad/index.html">
- <span class="en">Note Pad</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/SearchableDictionary/index.html">
- <span class="en">Searchable Dictionary</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/Snake/index.html">
- <span class="en">Snake</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/SoftKeyboard/index.html">
- <span class="en">Soft Keyboard</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/Wiktionary/index.html">
- <span class="en">Wiktionary</span>
- </a></li>
- <li><a href="<?cs var:toroot ?>guide/samples/WiktionarySimple/index.html">
- <span class="en">Wiktionary (Simplified)</span>
- </a></li>
- </ul>
- </li>
- </ul>
- </li>
-
<li>
<h2><span class="en">Appendix</span>
@@ -462,9 +387,6 @@
<li><a href="<?cs var:toroot ?>guide/appendix/glossary.html">
<span class="en">Glossary</span>
</a></li>
- <li><a href="<?cs var:toroot ?>guide/appendix/faq/index.html">
- <span class="en">FAQ</span>
- </a></li>
</ul>
</li>
diff --git a/docs/html/guide/index.jd b/docs/html/guide/index.jd
index ecbf97b..1674bc8 100644
--- a/docs/html/guide/index.jd
+++ b/docs/html/guide/index.jd
@@ -77,7 +77,7 @@
<p>
For additional help, consider joining one or more of the Android
discussion groups. Go to the
-<a href="{@docRoot}community/index.html">Community</a> tab above
+<a href="{@docRoot}resources/community-groups.html">Community</a> pages
for more information.
</p>
diff --git a/docs/html/guide/publishing/app-signing.jd b/docs/html/guide/publishing/app-signing.jd
index f0febc0..86dd155 100644
--- a/docs/html/guide/publishing/app-signing.jd
+++ b/docs/html/guide/publishing/app-signing.jd
@@ -268,7 +268,7 @@
<p>Note that, if your development machine is using a non-Gregorian locale, the build
tools may erroneously generate an already-expired debug certificate, so that you get an
error when trying to compile your application. For workaround information, see the
-troubleshooting topic <a href="{@docRoot}guide/appendix/faq/troubleshooting.html#signingcalendar">
+troubleshooting topic <a href="{@docRoot}resources/faq/troubleshooting.html#signingcalendar">
I can't compile my app because the build tools generated an expired debug
certificate</a>. </p>
diff --git a/docs/html/guide/publishing/versioning.jd b/docs/html/guide/publishing/versioning.jd
index 81e5ce0..1d55f8a 100644
--- a/docs/html/guide/publishing/versioning.jd
+++ b/docs/html/guide/publishing/versioning.jd
@@ -170,6 +170,6 @@
<code>android:minSdkVersion</code> as an attribute. </p>
<p>For more information, see the <a
-href="{@docRoot}guide/developing/manifest/uses-sdk-element.html"><code><uses-
-sdk></code></a> manifest element documentation and the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code><uses-sdk></code></a>
+manifest element documentation and the <a
href="{@docRoot}guide/appendix/api-levels.html">API Levels</a> document.</p>
diff --git a/docs/html/guide/topics/appwidgets/index.jd b/docs/html/guide/topics/appwidgets/index.jd
index 0ba05ac..f36b684 100644
--- a/docs/html/guide/topics/appwidgets/index.jd
+++ b/docs/html/guide/topics/appwidgets/index.jd
@@ -339,7 +339,7 @@
sample's AppWidgetProvider</a> for an example of an App Widget running a {@link android.app.Service}.</p>
<p>Also see the <a
-href="{@docRoot}guide/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.html">
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetProvider.html">
ExampleAppWidgetProvider.java</a> sample class.</p>
@@ -468,7 +468,7 @@
App Widget will not be added.</p>
<p>See the <a
-href="{@docRoot}guide/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.html">
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/appwidget/ExampleAppWidgetConfigure.html">
ExampleAppWidgetConfigure.java</a> sample class in ApiDemos for an example.</p>
diff --git a/docs/html/guide/topics/graphics/opengl.jd b/docs/html/guide/topics/graphics/opengl.jd
index 901980d..4d0b223 100644
--- a/docs/html/guide/topics/graphics/opengl.jd
+++ b/docs/html/guide/topics/graphics/opengl.jd
@@ -28,7 +28,7 @@
<p>For an example of this usage model (based on the classic GL ColorCube), showing how to use
it with threads can be found in
-<a href="{@docRoot}guide/samples/ApiDemos/src/com/example/android/apis/graphics/GLSurfaceViewActivity.html">com.android.samples.graphics.GLSurfaceViewActivity.java</a>.
+<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/graphics/GLSurfaceViewActivity.html">com.android.samples.graphics.GLSurfaceViewActivity.java</a>.
</p>
<p>Writing a summary of how to actually write 3D applications using OpenGL is
diff --git a/docs/html/guide/topics/intents/intents-filters.jd b/docs/html/guide/topics/intents/intents-filters.jd
index 962a001..7a7eb70 100644
--- a/docs/html/guide/topics/intents/intents-filters.jd
+++ b/docs/html/guide/topics/intents/intents-filters.jd
@@ -702,8 +702,8 @@
can find all the source files for this sample application, including its
manifest file, at {@code <sdk>/samples/NotePad/index.html}.
If you're viewing the documentation online, the source files are in the
-<a href="{@docRoot}guide/samples/index.html">Tutorials and Sample Code</a>
-section <a href="{@docRoot}guide/samples/NotePad/index.html">here</a>.)
+<a href="{@docRoot}resources/samples/index.html">Tutorials and Sample Code</a>
+section <a href="{@docRoot}resources/samples/NotePad/index.html">here</a>.)
</p>
<p>
diff --git a/docs/html/guide/topics/manifest/supports-screens-element.jd b/docs/html/guide/topics/manifest/supports-screens-element.jd
index 5494320..620d3b2 100644
--- a/docs/html/guide/topics/manifest/supports-screens-element.jd
+++ b/docs/html/guide/topics/manifest/supports-screens-element.jd
@@ -31,7 +31,7 @@
The screen density is expressed as dots-per-inch (dpi).</p>
<p>For more information, see
-<a href="{@docRoot}guide/practices/screens_suppport.html">Multiple Screens Support</a>.</p>
+<a href="{@docRoot}guide/practices/screens_support.html">Multiple Screens Support</a>.</p>
<dt>attributes:</dt>
diff --git a/docs/html/guide/topics/resources/localization.jd b/docs/html/guide/topics/resources/localization.jd
index f32f1ac..192695b 100755
--- a/docs/html/guide/topics/resources/localization.jd
+++ b/docs/html/guide/topics/resources/localization.jd
@@ -51,7 +51,7 @@
<h2>See also</h2>
<ol>
<li><a
-href="{@docRoot}guide/tutorials/localization/index.html">Hello, L10N Tutorial</a></li>
+href="{@docRoot}resources/tutorials/localization/index.html">Hello, L10N Tutorial</a></li>
<li><a href="resources-i18n.html">Resources</a></li>
<li><a href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a></li>
<li><a href="{@docRoot}reference/android/app/Activity.html#ActivityLifecycle">Activity Lifecycle</a></li>
@@ -90,7 +90,7 @@
</ul>
<p>The <a
-href="{@docRoot}guide/tutorials/localization/index.html">Hello, L10N
+href="{@docRoot}resources/tutorials/localization/index.html">Hello, L10N
</a> tutorial takes you through the steps of creating a simple localized
application that uses locale-specific resources in the way described in this
document. </p>
diff --git a/docs/html/guide/topics/ui/binding.jd b/docs/html/guide/topics/ui/binding.jd
index 4725321..6ac0bb0 100644
--- a/docs/html/guide/topics/ui/binding.jd
+++ b/docs/html/guide/topics/ui/binding.jd
@@ -13,9 +13,9 @@
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}guide/tutorials/views/hello-spinner.html">Hello Spinner tutorial</a></li>
- <li><a href="{@docRoot}guide/tutorials/views/hello-listview.html">Hello ListView tutorial</a></li>
- <li><a href="{@docRoot}guide/tutorials/views/hello-gridview.html">Hello GridView tutorial</a></li>
+ <li><a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Hello Spinner tutorial</a></li>
+ <li><a href="{@docRoot}resources/tutorials/views/hello-listview.html">Hello ListView tutorial</a></li>
+ <li><a href="{@docRoot}resources/tutorials/views/hello-gridview.html">Hello GridView tutorial</a></li>
</ol>
</div>
</div>
@@ -107,7 +107,7 @@
<div class="special">
<p>For more discussion on how to create different AdapterViews, read the following tutorials:
-<a href="{@docRoot}guide/tutorials/views/hello-spinner.html">Hello Spinner</a>,
-<a href="{@docRoot}guide/tutorials/views/hello-listview.html">Hello ListView</a>, and
-<a href="{@docRoot}guide/tutorials/views/hello-gridview.html">Hello GridView</a>.
+<a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Hello Spinner</a>,
+<a href="{@docRoot}resources/tutorials/views/hello-listview.html">Hello ListView</a>, and
+<a href="{@docRoot}resources/tutorials/views/hello-gridview.html">Hello GridView</a>.
</div>
diff --git a/docs/html/guide/topics/ui/custom-components.jd b/docs/html/guide/topics/ui/custom-components.jd
index 6027a82..900e08b 100644
--- a/docs/html/guide/topics/ui/custom-components.jd
+++ b/docs/html/guide/topics/ui/custom-components.jd
@@ -61,7 +61,7 @@
</li>
<li>
You could override the way that an EditText component is rendered on the screen
- (the <a href="{@docRoot}guide/samples/NotePad/index.html">Notepad Tutorial</a> uses this to good effect,
+ (the <a href="{@docRoot}resources/samples/NotePad/index.html">Notepad Tutorial</a> uses this to good effect,
to create a lined-notepad page).
</li>
<li>
@@ -318,9 +318,9 @@
<h3 id="customexample">A Custom View Example</h3>
<p>The CustomView sample in the
-<a href="{@docRoot}guide/samples/ApiDemos/index.html">API Demos</a> provides an example
+<a href="{@docRoot}resources/samples/ApiDemos/index.html">API Demos</a> provides an example
of a customized View. The custom View is defined in the
-<a href="{@docRoot}guide/samples/ApiDemos/src/com/example/android/apis/view/LabelView.html">LabelView</a>
+<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/LabelView.html">LabelView</a>
class.</p>
<p>The LabelView sample demonstrates a number of different aspects of custom components:</p>
<ul>
@@ -339,7 +339,7 @@
provided canvas.</li>
</ul>
<p>You can see some sample usages of the LabelView custom View in
-<a href="{@docRoot}guide/samples/ApiDemos/res/layout/custom_view_1.html">custom_view_1.xml</a>
+<a href="{@docRoot}resources/samples/ApiDemos/res/layout/custom_view_1.html">custom_view_1.xml</a>
from the samples. In particular, you can see a mix of both <code>android:</code>
namespace parameters and custom <code>app:</code> namespace parameters. These
<code>app:</code> parameters are the custom ones that the LabelView recognizes
@@ -446,7 +446,7 @@
specialized class in the View hierarchy, you can also get a lot of behavior for
free that probably does exactly what you want.</p>
<p>For example, the SDK includes a <a
-href="{@docRoot}guide/samples/NotePad/index.html">NotePad application</a> in the
+href="{@docRoot}resources/samples/NotePad/index.html">NotePad application</a> in the
samples. This demonstrates many aspects of using the Android platform, among
them is extending an EditText View to make a lined notepad. This is not a
perfect example, and the APIs for doing this might change from this early
@@ -455,7 +455,7 @@
NotePad sample into Eclipse (or
just look at the source using the link provided). In particular look at the definition of
<code>MyEditText</code> in the <a
-href="{@docRoot}guide/samples/NotePad/src/com/example/android/notepad/NoteEditor.html">NoteEditor.java</a>
+href="{@docRoot}resources/samples/NotePad/src/com/example/android/notepad/NoteEditor.html">NoteEditor.java</a>
file.</p>
<p>Some points to note here</p>
<ol>
diff --git a/docs/html/guide/topics/ui/declaring-layout.jd b/docs/html/guide/topics/ui/declaring-layout.jd
index fdd5ddd..662efbf 100644
--- a/docs/html/guide/topics/ui/declaring-layout.jd
+++ b/docs/html/guide/topics/ui/declaring-layout.jd
@@ -65,7 +65,7 @@
<p class="note"><strong>Tip:</strong> Learn more about different layout types in <a href="{@docRoot}guide/topics/ui/layout-objects.html">Common
Layout Objects</a>. There are also a collection of tutorials on building various layouts in the
-<a href="{@docRoot}guide/tutorials/views/index.html">Hello Views</a> tutorial guide.</p>
+<a href="{@docRoot}resources/tutorials/views/index.html">Hello Views</a> tutorial guide.</p>
<h2 id="write">Write the XML</h2>
diff --git a/docs/html/guide/topics/ui/dialogs.jd b/docs/html/guide/topics/ui/dialogs.jd
index c476b0d..74b544b 100644
--- a/docs/html/guide/topics/ui/dialogs.jd
+++ b/docs/html/guide/topics/ui/dialogs.jd
@@ -47,10 +47,10 @@
See <a href="#ProgressDialog">Creating a ProgressDialog</a> below.</dd>
<dt>{@link android.app.DatePickerDialog}</dt>
<dd>A dialog that allows the user to select a date. See the
- <a href="{@docRoot}guide/tutorials/views/hello-datepicker.html">Hello DatePicker</a> tutorial.</dd>
+ <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Hello DatePicker</a> tutorial.</dd>
<dt>{@link android.app.TimePickerDialog}</dt>
<dd>A dialog that allows the user to select a time. See the
- <a href="{@docRoot}guide/tutorials/views/hello-timepicker.html">Hello TimePicker</a> tutorial.</dd>
+ <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Hello TimePicker</a> tutorial.</dd>
</dl>
<p>If you would like to customize your own dialog, you can extend the
diff --git a/docs/html/guide/topics/ui/layout-objects.jd b/docs/html/guide/topics/ui/layout-objects.jd
index bb13a188..345e9b3 100644
--- a/docs/html/guide/topics/ui/layout-objects.jd
+++ b/docs/html/guide/topics/ui/layout-objects.jd
@@ -19,7 +19,7 @@
<p>This section describes some of the more common types of layout objects
to use in your applications. Like all layouts, they are subclasses of {@link android.view.ViewGroup ViewGroup}.</p>
-<p>Also see the <a href="{@docRoot}guide/tutorials/views/index.html">Hello Views</a> tutorials for
+<p>Also see the <a href="{@docRoot}resources/tutorials/views/index.html">Hello Views</a> tutorials for
some guidance on using more Android View layouts.</p>
<h2 id="framelayout">FrameLayout</h2>
@@ -83,7 +83,7 @@
<code>android:baselineAligned="false"</code> in the layout XML. </p>
<p>To view other sample code, see the
-<a href="{@docRoot}guide/tutorials/views/hello-linearlayout.html">Hello LinearLayout</a> tutorial.</p>
+<a href="{@docRoot}resources/tutorials/views/hello-linearlayout.html">Hello LinearLayout</a> tutorial.</p>
<h2 id="tablelayout">TableLayout</h2>
@@ -138,7 +138,7 @@
fits the screen. See the {@link android.widget.TableLayout TableLayout reference}
documentation for more details. </p>
-<p>To view sample code, see the <a href="{@docRoot}guide/tutorials/views/hello-tablelayout.html">Hello
+<p>To view sample code, see the <a href="{@docRoot}resources/tutorials/views/hello-tablelayout.html">Hello
TableLayout</a> tutorial.</p>
@@ -208,7 +208,7 @@
For example, assigning the parameter <code>toLeft="my_button"</code> to a TextView would place the TextView to
the left of the View with the ID <var>my_button</var> (which must be written in the XML <em>before</em> the TextView). </p>
-<p>To view this sample code, see the <a href="{@docRoot}guide/tutorials/views/hello-relativelayout.html">Hello
+<p>To view this sample code, see the <a href="{@docRoot}resources/tutorials/views/hello-relativelayout.html">Hello
RelativeLayout</a> tutorial.</p>
diff --git a/docs/html/guide/topics/ui/menus.jd b/docs/html/guide/topics/ui/menus.jd
index 0129e8f..cf3c7de 100644
--- a/docs/html/guide/topics/ui/menus.jd
+++ b/docs/html/guide/topics/ui/menus.jd
@@ -180,7 +180,7 @@
<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}guide/tutorials/notepad/index.html">Notepad application</a>
+<a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad application</a>
to add a context menu for each note in the list:</p>
<pre>
public void onCreateContextMenu(ContextMenu menu, View v,
@@ -518,5 +518,5 @@
<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}guide/samples/NotePad/index.html">Note Pad</a>
+<a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a>
sample code.</p>
diff --git a/docs/html/guide/topics/ui/ui-events.jd b/docs/html/guide/topics/ui/ui-events.jd
index 6b30e44..ccef64f 100644
--- a/docs/html/guide/topics/ui/ui-events.jd
+++ b/docs/html/guide/topics/ui/ui-events.jd
@@ -15,7 +15,7 @@
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}guide/tutorials/views/hello-formstuff.html">Hello Form Stuff tutorial</a></li>
+ <li><a href="{@docRoot}resources/tutorials/views/hello-formstuff.html">Hello Form Stuff tutorial</a></li>
</ol>
</div>
</div>
diff --git a/docs/html/guide/topics/views/custom-views.jd b/docs/html/guide/topics/views/custom-views.jd
index 4e0e164..8589b1a 100644
--- a/docs/html/guide/topics/views/custom-views.jd
+++ b/docs/html/guide/topics/views/custom-views.jd
@@ -40,7 +40,7 @@
</li>
<li>
You could override the way that an EditText component is rendered on the screen
- (the <a href="{@docRoot}guide/samples/NotePad/">Notepad sample</a> uses this to good effect,
+ (the <a href="{@docRoot}resources/samples/NotePad/">Notepad sample</a> uses this to good effect,
to create a lined-notepad page).
</li>
<li>
diff --git a/docs/html/intl/ja/guide/basics/what-is-android.jd b/docs/html/intl/ja/guide/basics/what-is-android.jd
index 89558a0..fcf670d 100644
--- a/docs/html/intl/ja/guide/basics/what-is-android.jd
+++ b/docs/html/intl/ja/guide/basics/what-is-android.jd
@@ -39,7 +39,7 @@
<p>アプリケーションの基盤となるのは、次のサービスとシステムのセットです:
<ul>
<li>アプリケーションの構築を可能にする、拡張可能で豊富な<a
- href="{@docRoot}guide/tutorials/views/index.html">ビュー</a>のセット。ビューには、リスト、グリッド、テキスト ボックス、ボタンだけでなく、埋め込み可能なウェブブラウザも含まれます。</li>
+ href="{@docRoot}resources/tutorials/views/index.html">ビュー</a>のセット。ビューには、リスト、グリッド、テキスト ボックス、ボタンだけでなく、埋め込み可能なウェブブラウザも含まれます。</li>
<li><a href="{@docRoot}guide/topics/providers/content-providers.html">コンテンツ プロバイダ</a>を使用すると、アプリケーションのデータ(たとえば、連絡先アプリケーション)に、別のアプリケーションからアクセスしたり、データを共有させることができます。</li> <li><a
href="{@docRoot}guide/topics/resources/resources-i18n.html">リソース マネージャ</a>は、ローカライズされた文字列、グラフィックス、レイアウト ファイルなどのコード以外のリソースへのアクセスを提供します。</li>
<li>{@link android.app.NotificationManager 通知マネージャ}を使用すると、すべてのアプリケーションからステータス バーにカスタマイズした警告を表示することができます。</li>
@@ -47,7 +47,7 @@
</ul>
<p>アプリケーションの簡単な説明と詳細については、<a
-href="{@docRoot}guide/tutorials/notepad/index.html">Notepad チュートリアル</a>をご覧ください。</p>
+href="{@docRoot}resources/tutorials/notepad/index.html">Notepad チュートリアル</a>をご覧ください。</p>
<a name="libraries" id="libraries"></a>
<h2>ライブラリ</h2>
diff --git a/docs/html/intl/ja/guide/index.jd b/docs/html/intl/ja/guide/index.jd
index 73ca18a..5d35e0a 100644
--- a/docs/html/intl/ja/guide/index.jd
+++ b/docs/html/intl/ja/guide/index.jd
@@ -41,12 +41,12 @@
</p>
<p>
-SDK のダウンロード後は、まずはじめにデベロッパー ガイドを参照してください。コードを実際に見てみることから始めたい場合は、簡単な <a href="{@docRoot}guide/tutorials/hello-world.html">Hello World</a> チュートリアルを参照してください。Android プラットフォーム向けに作成された標準的な「Hello, World」アプリケーションについて説明しています。<a href="{@docRoot}guide/topics/fundamentals.html">アプリケーションの基礎</a>ドキュメントは、アプリケーション フレームワークを理解したいユーザーに最適な出発点となります。
+SDK のダウンロード後は、まずはじめにデベロッパー ガイドを参照してください。コードを実際に見てみることから始めたい場合は、簡単な <a href="{@docRoot}resources/tutorials/hello-world.html">Hello World</a> チュートリアルを参照してください。Android プラットフォーム向けに作成された標準的な「Hello, World」アプリケーションについて説明しています。<a href="{@docRoot}guide/topics/fundamentals.html">アプリケーションの基礎</a>ドキュメントは、アプリケーション フレームワークを理解したいユーザーに最適な出発点となります。
</p>
<p>
-不明点などがある場合は、Android ディスカッション グループへの参加をおすすめします。詳しくは上部にある [<a href="{@docRoot}community/index.html">コミュニティ</a>] タブをご覧ください。
+不明点などがある場合は、Android ディスカッション グループへの参加をおすすめします。詳しくは上部にある [<a href="{@docRoot}resources/community-groups.html">コミュニティ</a>] タブをご覧ください。
</p>
<p>デベロッパー ガイドの別のページに移動してから、このページに戻るには、[デベロッパー ガイド] タブをクリックします。 </p>
\ No newline at end of file
diff --git a/docs/html/intl/ja/guide/publishing/app-signing.jd b/docs/html/intl/ja/guide/publishing/app-signing.jd
index 23d8cf7..710591d 100644
--- a/docs/html/intl/ja/guide/publishing/app-signing.jd
+++ b/docs/html/intl/ja/guide/publishing/app-signing.jd
@@ -153,7 +153,7 @@
<p>次にビルドを行うと、ビルド ツールは新しいキーストアとデバッグ キーを再度生成します。</p>
-<p>開発コンピュータがグレゴリオ暦以外のロケールを使用している場合、ビルド ツールが誤って期限切れのデバッグ証明書を生成することがあります。このため、アプリケーションをコンパイルしようとするとエラーが発生します。解決策については、トラブルシューティング トピックの <a href="{@docRoot}guide/appendix/faq/troubleshooting.html#signingcalendar">ビルド ツールが期限切れのデバッグ証明書を生成するため、アプリケーションがコンパイルできない</a> をご覧ください。 </p>
+<p>開発コンピュータがグレゴリオ暦以外のロケールを使用している場合、ビルド ツールが誤って期限切れのデバッグ証明書を生成することがあります。このため、アプリケーションをコンパイルしようとするとエラーが発生します。解決策については、トラブルシューティング トピックの <a href="{@docRoot}resources/faq/troubleshooting.html#signingcalendar">ビルド ツールが期限切れのデバッグ証明書を生成するため、アプリケーションがコンパイルできない</a> をご覧ください。 </p>
<h2 id="releasemode">公開リリースへの署名</h2>
diff --git a/docs/html/intl/ja/resources/community-groups.jd b/docs/html/intl/ja/resources/community-groups.jd
new file mode 100644
index 0000000..c99b1f8
--- /dev/null
+++ b/docs/html/intl/ja/resources/community-groups.jd
@@ -0,0 +1,116 @@
+community=true
+page.title=コミュニティ
+@jd:body
+
+ <div id="mainBodyFluid">
+ <h1>コミュニティ</h1>
+ <p>Android デベロッパー コミュニティへようこそ。コミュニティでのディスカッションにぜひ参加してください。投稿する前に、コミュニティ ガイドラインが記載されている<a href="http://source.android.com/discuss/android-discussion-groups-charter">グループの趣意</a>をお読みください。</p>
+
+<p class="note"><strong>注:</strong> Android ソース コード(アプリケーション開発ではなく)に関するディスカッションは、<a href="http://source.android.com/discuss">オープンソース プロジェクトのメーリング リスト</a>(英語)を参照してください。</p>
+
+<p style="margin-bottom:.5em"><strong>目次</strong></p>
+<ol class="toc">
+ <li><a href="#BeforeYouPost">質問を投稿する前に</a></li>
+ <li><a href="#ApplicationDeveloperLists">アプリケーション デベロッパー メーリング リスト</a></li>
+ <li><a href="#UsingEmail">メーリング リストにメールを使用</a></li>
+ <li><a href="#UsingIRC">IRC の使用</a></li>
+</ol>
+
+<h2 id="BeforeYouPost">質問を投稿する前に</h2>
+<p>投稿を作成する前に、下記をお試しください:</p>
+
+<ol>
+<li><a href="{@docRoot}resources/faq/index.html">よくある質問を参照します</a>。Android アプリケーションの開発について非常に一般的な質問が、この一覧に記載されており、頻繁に更新されています。</li>
+<li><strong>Android のメイン サイトの検索バー(このページの上部にあるのと同じもの)に、調べたいキーワードを入力してください</strong>。この検索は、サイト、ドキュメント、ブログに含まれるすべてのコンテンツの他に、すべてのグループで以前行われたすべてのディスカッションを網羅しています。誰か他の人が、以前にも同じ問題に遭遇した可能性は大いにあります。</li>
+<li><b>メーリング リストのアーカイブを検索</b>して、同じ質問に関するディスカッションが既に存在しないか調べてください。
+ </li>
+</ol>
+
+<p>質問への答えが見つからない場合、コミュニティで質問することをおすすめします。投稿する際は、次の手順に従ってください。
+<ol>
+<li>コミュニティ ガイドラインが記載されている<b><a href="http://sites.google.com/a/android.com/opensource/discuss/android-discussion-groups-charter">Android メーリングリストの趣意</a></b>をお読みください。
+</li>
+<li><b>質問に最適なメーリング リストを選択してください</b>。後述するように、デベロッパー向けのメーリング リストは何種類かに分かれています。</li>
+<li>
+ <b>質問の内容を明確に</b>。明確な質問は、回答者と、将来情報を探そうとする人の双方にとって有益です。</li>
+<li><b>投稿は詳しく書いてください</b>。回答者の人たちが問題を理解するのに役立ちます。コードやログのスニペット、スクリーンショットへのリンクを含めることも有用です。質問をわかりやすく表現するための詳しいガイドラインは、<a href="http://www.catb.org/%7Eesr/faqs/smart-questions.html">賢い質問のしかた</a>(英語)をご覧ください。
+ </li>
+</ol>
+
+
+<h3 id="ApplicationDeveloperLists">アプリケーション デベロッパー メーリング リスト</h3>
+<ul>
+
+<li><b>Android SDK Japan</b> - Android SDK に関する質問と答え、提案とフィードバックを交換できる場です。
+<ul>
+<li>Google グループで登録: <a href="http://groups.google.co.jp/group/android-sdk-japan?hl=ja">android-sdk-japan</a></li>
+<li>メールで登録: <a href="mailto:android-sdk-japan-subscribe@googlegroups.com">android-sdk-japan-subscribe@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android 初心者向け</b> - Android アプリケーションの開発初心者向けです。Android SDK と基本的な Android API の利用方法について学習したい場合は、このメーリング リストから始めてください。このメーリング リストには、SDK を利用するデベロッパーの初歩的なディスカッションの場所です。Android プラットフォームで初めてアプリケーションを作成して実行する際は、非常に有益な情報を得ることができるでしょう。開発環境のインストール方法についての質問を投稿したり、Android 開発の初歩(初めて作成するユーザー インターフェース、権限、Android ファイルシステムでのファイル、Android マーケットでのアプリケーションなど)について教えてもらうことができます。新たに質問する前に、必ず最初にアーカイブを確認してください。高度な内容の質問の場合はここでは質問せず、android-developers メーリング リストで質問してください。また使用に関する質問は、android-discuss メーリング リストの方が適しています。
+<ul>
+<li>Google グループで登録: <a href="http://groups.google.com/group/android-beginners?hl=ja">android-beginners</a></li>
+<li>メールで登録: <a href="mailto:android-beginners-subscribe@googlegroups.com">android-beginners-subscribe@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android デベロッパー向け</b> - Android アプリケーション デベロッパーとして経験を積むにつれ、Android アプリケーション開発の基本を把握して、SDK を使いこなせるようになります。今度は、より高度な内容について質問する必要があります。アプリケーションのトラブルシューティング、実装へのアドバイス、アプリケーションのパフォーマンスやユーザー エクスペリエンスを改良するテクニックに関する質問には、次のメーリング リストが役立ちます。使用に関する問題(android-discuss をご利用ください)や、Android SDK を使用する際の初歩的質問(android-beginners をご利用ください)についてのディスカッションの場所ではありません。
+<ul>
+<li>Google グループで登録: <a href="http://groups.google.com/group/android-developers?hl=ja">android-developers</a></li>
+<li>メールで登録: <a href="mailto:android-developers-subscribe@googlegroups.com">android-developers-subscribe@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android ディスカッション</b> - Android に関する「井戸端会議」です。ここでは、Android プラットフォームへのアイデア、自分のアプリケーションの公表、Android 携帯端末に関するディスカッション、コミュニティ リソースなど、Android に関することなら何でも投稿可能です。ただし他のメーリング リストに該当する内容の場合は、そのメーリング リストに投稿することをおすすめします。質問のテーマが限定されている場所の方が、より多くの回答を得ることができるでしょう。
+<ul>
+<li>Google グループで登録: <a href="http://groups.google.com/group/android-discuss?hl=ja">android-discuss</a></li>
+<li>メールで登録: <a href="mailto:android-discuss-subscribe@googlegroups.com">android-discuss-subscribe@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android セキュリティ ディスカッション</b> - 安全な開発、新たに発生したセキュリティの問題、Android デベロッパー向けの Android デベロッパーによるベスト プラクティスについて自由にディスカッションを行える場所です。メーリング リストで脆弱性を直接公開することは、すべての Android ユーザーを危険にさらすことになるので、避けてください。
+<ul>
+<li>Google グループで登録: <a href="http://groups.google.com/group/android-security-discuss?hl=ja">android-security-discuss</a></li>
+<li>メールで登録: <a href="mailto:android-security-discuss@googlegroups.com">android-security-discuss@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android セキュリティに関する発表</b> - Android セキュリティ チームがセキュリティ関連の発表を行う、小規模なグループです。
+<ul>
+<li>Google グループで登録: <a href="http://groups.google.com/group/android-security-announce?hl=ja">android-security-announce</a></li>
+<li>メールで登録: <a href="mailto:android-security-announce-subscribe@googlegroups.com">android-security-announce-subscribe@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android マーケット ヘルプフォーラム</b> - Android マーケットに関する質問や問題の報告をするための、ウェブベースのディスカッション フォーラムです。
+<ul>
+<li>URL: <a href="http://www.google.com/support/forum/p/Android+Market?hl=ja">http://www.google.com/support/forum/p/Android+Market?hl=ja</a></li>
+</ul>
+</li>
+
+</ul>
+
+
+
+<h2 id="UsingEmail">メーリング リストにメールを使用</h2>
+<p><a href="http://groups.google.com/">Google グループ</a> のサイトを使用する代わりに、メール クライアントを使用して、メーリング リストに投稿することも可能です。</p>
+<p>Google グループのサイトを使用せずに、グループに登録するには、上記の「メールで登録」のリンクを使用します。</p>
+<p>メーリング リストへの投稿をメールで受信するように設定する方法は、次のとおりです:</p>
+
+<ol><li>Google グループ サイトから、グループにログインします。たとえば android-framework グループには <a href="http://groups.google.com/group/android-framework?hl=ja">http://groups.google.com/group/android-framework?hl=ja</a> にアクセスします。</li>
+<li>右側の [メンバーステータスを編集] をクリックします。</li>
+<li>[このグループの閲覧方法] で、メール オプションのいずれかを選択します。 </li>
+</ol>
+
+<h2 id="UsingIRC">IRC の使用</h2>
+<p>Android コミュニティは irc.freenode.net サーバーの #android チャンネルを使用しています。
+</p>
+
+
+
+
+
+
+
+</div>
diff --git a/docs/html/intl/ja/resources/tutorials/hello-world.jd b/docs/html/intl/ja/resources/tutorials/hello-world.jd
new file mode 100644
index 0000000..c12965c
--- /dev/null
+++ b/docs/html/intl/ja/resources/tutorials/hello-world.jd
@@ -0,0 +1,375 @@
+page.title=Hello, World
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>このドキュメントの内容</h2>
+ <ol>
+ <li><a href="#avd">AVD の作成</a></li>
+ <li><a href="#create">プロジェクトを作成する</a></li>
+ <li><a href="#ui">UI を構築する</a></li>
+ <li><a href="#run">コードを実行する</a></li>
+ <li><a href="#upgrading">UI を XML レイアウトにアップグレードする</a></li>
+ <li><a href="#debugging">プロジェクトをデバッグする</a></li>
+ <li><a href="#noeclipse">Eclipse を使用せずにプロジェクトを作成する</a></li>
+ </ol>
+ </div>
+</div>
+
+<p>デベロッパーにとって、開発フレームワークの第一印象は、どれだけ簡単に「Hello, World」を記述できるかで決まります。Android では、非常に簡単に記述できます。総合開発環境として Eclipse を使用している場合には、開発は特に簡単です。プロジェクトの作成と管理に使用できる便利なプラグインが用意されており、開発サイクルを大幅にスピードアップできるためです。</p>
+
+<p>Eclipse を使用していない場合でも問題ありません。<a href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>に慣れてから、このチュートリアルに戻り、Eclipse に関する部分以外を参考にしてください。</p>
+
+<p>開始する前に、最新の SDK がインストールされている必要があります。また、Eclipse を使用する場合には、ADT プラグインもインストールされている必要があります。これらのプログラムがインストールされていない場合は、「<a href="{@docRoot}sdk/installing.html">Installing the Android SDK</a>」を参考にインストールを実行して、完了後にこのチュートリアルに戻ってください。</p>
+
+<h2 id="avd">AVD の作成</h2>
+
+<div class="sidebox-wrapper">
+ <div class="sidebox-inner">
+ <p>AVD の使用方法と使用可能なオプションについて詳しくは、<a href="{@docRoot}guide/developing/tools/avd.html">Android 仮想デバイス</a> のドキュメントを参照してください。</p>
+ </div>
+</div>
+
+<p>このチュートリアルでは、開発したアプリケーションを Android エミュレータで実行します。エミュレータを起動するには、事前に Android 仮想デバイス(AVD)を作成する必要があります。AVD は、エミュレータが使用するシステム イメージとデバイスの設定を定義するものです。</p>
+
+<p>AVD を作成するには、Android SDK に含まれている「android」ツールを使用します。コマンド プロンプトまたはターミナルを開き、SDK パッケージの中の <code>tools/</code> ディレクトリに移動して、次のコマンドを実行します。
+<pre>
+android create avd --target 2 --name my_avd
+</pre>
+
+<p>カスタム ハードウェア プロファイルを作成するかどうかを尋ねられます。ここではひとまず、リターン キーを押してスキップします(デフォルトの回答は「No」となっています)以上で AVD の作成は終了です。この作業により、Android 1.5 プラットフォームを使用する「my_avd」という名前の AVD が構成されました。これで、AVD をエミュレータで使用できる状態になりました。</p>
+
+<p>上記のコマンドで使用した <code>--target</code> オプションは、エミュレータを実行する配備ターゲットを指定するもので、必須オプションです。<code>--name</code> オプションは新規 AVD の名前を定義するもので、これも必須オプションです。</p>
+
+
+<h2 id="create">新規 Android プロジェクトを作成する</h2>
+
+<p>AVD を作成したら、次は Eclipse 内で新規 Android プロジェクトを開始します。</p>
+
+<ol>
+ <li>Eclipse で、<strong>[[]ファイル(File)] > [[]新規(New)] > [[]プロジェクト(Project)]</strong> を選択します。
+ <p>ADT Plugin for Eclipse が正常にインストールされていれば、表示されるダイアログに、「Android」というラベルの付いたフォルダと、その中の「Android プロジェクト(Android Project)」が表示されます(1 つまたは複数の Android プロジェクトを作成した後は、「Android XML File」というエントリも表示されるようになります)。</p>
+ </li>
+
+ <li>「Android プロジェクト(Android Project)」を選択して、[[]<strong>次へ(Next)</strong>] をクリックします。<br/><a href="images/hello_world_0.png"><img src="images/hello_world_0.png" style="height:230px" alt="" /></a>
+ </li>
+
+ <li>プロジェクトの詳細項目に以下の値を入力します。
+ <ul>
+ <li><em>プロジェクト名(Project name):</em> HelloAndroid</li>
+ <li><em>アプリケーション名(Application name):</em> Hello, Android</li>
+ <li><em>パッケージ名(Package name):</em> com.example.helloandroid(または自分のプライベート ネームスペース)</li>
+ <li><em>アクティビティを作成(Create Activity):</em> HelloAndroid</li>
+ <li><em>SDK の最小バージョン(Min SDK Version):</em> 2</li>
+ </ul>
+ <p>[[]<strong>完了(Finish)</strong>] をクリックします。</p>
+
+ <a href="images/hello_world_1.png"><img src="images/hello_world_1.png" style="height:230px" alt="" /></a>
+
+ <p>各フィールドの説明は以下のとおりです。</p>
+
+ <dl>
+ <dt><em>プロジェクト名(Project Name)</em></dt>
+ <dd>Eclipse のプロジェクト名。プロジェクト ファイルを格納するディレクトリの名前です。</dd>
+ <dt><em>アプリケーション名(Application Name)</em></dt>
+ <dd>アプリケーション名はユーザーにわかりやすいアプリケーションのタイトルにします。この名前が Android 携帯端末に表示されます。</dd>
+ <dt><em>パッケージ名(Package Name)</em></dt>
+ <dd>作成したすべてのソース コードを格納するパッケージ ネームスペースです(Java プログラミング言語で作成するパッケージと同じルールに従います)。また、これにより、スタブ Activity が生成されるパッケージの名前も設定されます。
+ <p>パッケージ名は Android システムにインストールされたすべてのパッケージに共通して固有のものでなければなりません。このため、作成するアプリケーションに標準的なドメイン スタイルのパッケージを使用することが非常に重要です。上記の例では、「com.example」というネームスペースを使用しています。これはサンプル ドキュメント用のネームスペースです。実際にアプリケーションを作成する際には、所属する組織または法人に適切なネームスペースを使用します。</p></dd>
+ <dt><em>アクティビティを作成(Create Activity)</em></dt>
+ <dd>プラグインによって生成されるクラス スタブの名前です。クラス スタブは Android の {@link android.app.Activity} クラスのサブクラスとなります。アクティビティとは単に、実行して何らかの処理を行うことができるクラスを意味します。選択に応じて UI を作成することもできます(ただし必須ではありません)。チェックボックスになっていることからわかるように、これは任意選択の項目です。しかし、実際にはほとんどのアプリケーションでは、アクティビティをアプリケーションの基盤として使用しています。</dd>
+ <dt><em>SDK の最小バージョン(Min SDK Version)</em></dt>
+ <dd>作成するアプリケーションが必要とする最小 API レベルを指定する値です。ここに入力した API レベルが、選択可能なターゲットのいずれかで提供される API レベルと一致する場合は、ビルド ターゲットが自動的に選択されます(この例では、API レベルに「2」と入力するとターゲット Android 1.1 が選択されます)。Android システム イメージと Android SDK それぞれの新しいバージョンでは、API に追加または変更が加えられている可能性があります。追加または変更が加えられている場合、新しい API レベルがシステム イメージに割り当てられ、どのアプリケーションの実行を許可するかが規制されます。アプリケーションで必要な API レベルがデバイスでサポートされるレベルよりも<em>高い</em>場合、アプリケーションはインストールされません。</dd>
+ </dl>
+
+ <p><em>その他のフィールド</em>: 「デフォルト ロケーションの使用」チェックボックスでは、プロジェクトのファイルが生成され保存されるディスク上の場所を変更することができます。「ビルド ターゲット」は、作成するアプリケーションがコンパイルされるときにターゲットとするプラットフォームです(この項目は [[]SDK の最小バージョン(Min SDK Version)] の入力値に基づいて自動的に選択されます)。</p>
+
+ <p class="note">ここで、選択した「ビルド ターゲット」で Android 1.1 プラットフォームが使用されることに注目してください。これは、作成するアプリケーションが Android 1.1 プラットフォーム ライブラリをターゲットとしてコンパイルされることを意味します。先ほど作成した AVD は Android 1.5 プラットフォームで実行されます。バージョンの数字が一致しませんが、Android アプリケーションには上方互換性があるため、1.1 プラットフォーム ライブラリをターゲットとして構築されたアプリケーションでも 1.5 プラットフォームで正常に動作します。ただしその逆の場合は正常に動作しません。</p>
+ </li>
+</ol>
+
+<p>さて、これで Android プロジェクトを使用できる状態になりました。プロジェクトは左側のパッケージ エクスプローラー(Package Explorer)で表示できます。<em>「HelloAndroid」 > 「src」 > 「com.example.helloandroid」</em> の中にある <code>HelloAndroid.java</code> ファイルを開きます。ファイルの内容は次のようになっています。</p>
+
+<pre>
+package com.example.helloandroid;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class HelloAndroid extends Activity {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ }
+}</pre>
+
+<p>クラスが {@link android.app.Activity} クラスに基づいていることに注目してください。アクティビティ(Activity)とは、処理を実行するために使用される単体のアプリケーション エンティティです。1 つのアプリケーションにはいくつものアクティビティが含まれる場合がありますが、ユーザーが一度に操作するのは 1 つのアクティビティです。アクティビティが開始すると、Android システムによって {@link android.app.Activity#onCreate(Bundle) onCreate()} メソッドが呼び出されます。このタイミングですべての初期化と UI セットアップを実行します。アクティビティにユーザー インターフェースは必須ではありませんが、通常はユーザー インターフェースを装備します。</p>
+
+<p>では、コードを変更してみましょう。 </p>
+
+
+<h2 id="ui">UI を構築する</h2>
+
+<p>下記の変更済みのコードを参照して、お手元の HelloAndroid クラスに同じ変更を加えてみてください。太字の部分が追加された行です。</p>
+
+<pre>
+package com.android.helloandroid;
+
+import android.app.Activity;
+import android.os.Bundle;
+<strong>import android.widget.TextView;</strong>
+
+public class HelloAndroid extends Activity {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ <strong>TextView tv = new TextView(this);
+ tv.setText("Hello, Android");
+ setContentView(tv);</strong>
+ }
+}</pre>
+
+<p class="note"><strong>ヒント:</strong> プロジェクトにインポート パッケージを簡単に追加できる方法として、<strong>Ctrl+Shift+O</strong>(Mac では <strong>コマンド+Shift+O</strong>)を押す方法があります。これは、コードの記述に基づいて足りないパッケージを特定して追加する Eclipse のショートカット キーです。</p>
+
+<p>Android のユーザー インターフェースは、「ビュー(Views)」と呼ばれるオブジェクトの階層で構成されています。{@link android.view.View} は、UI レイアウト内でボタン、画像、または(このサンプルのように)テキスト ラベルといった要素として使用される、描画可能なオブジェクトです。これらのオブジェクトのそれぞれが View クラスのサブクラスであり、テキストを処理するサブクラスは {@link android.widget.TextView} です。</p>
+
+<p>この変更では、クラス コンストラクタを使用して TextView を作成します。このクラス コンストラクタは、パラメータとして Android {@link android.content.Context} インスタンスを受け入れます。Context とは、システムへのハンドルであり、リソースの解決、データベースや設定へのアクセスの取得などのサービスを提供します。Activity クラスは Context を継承します。作成する HelloAndroid クラスは、Activity のサブクラスであるため、Context でもあります。したがって、<code>this</code> を Context 参照として TextView に引き渡すことができます。</p>
+
+<p>次に、{@link android.widget.TextView setText(CharSequence) setText()} を使用してテキスト コンテンツを定義します。</p>
+
+<p>最後に、そのコンテンツが Activity UI のコンテンツとして表示されるように、TextView を {@link android.app.Activity#setContentView(View) setContentView()} に引き渡します。Activity によってこのメソッドが呼び出されなければ、UI は表示されず、空白の画面が表示されます。</p>
+
+<p>これで、Android で「Hello, World」が表示されるようになりました。次の手順はもちろん、アプリケーションの実行です。</p>
+
+
+<h2 id="run">アプリケーションを実行する</h2>
+
+<p>Eclipse プラグインでは、非常に簡単にアプリケーションを実行できます。</p>
+
+<ol>
+ <li><strong>[[]実行] > [[]実行]</strong> を選択します。</li>
+ <li>「Android Application」を選択します。</li>
+</ol>
+
+<div class="sidebox-wrapper">
+ <div class="sidebox-inner">
+ <p>Eclipse での起動構成の作成と編集について詳しくは、「<a href="{@docRoot}guide/developing/eclipse-adt.html#RunConfig">ADT を使用した Eclipse での開発</a>」を参照してください。</p>
+ </div>
+</div>
+
+<p>Eclipse ADT によって自動的にプロジェクトの新規起動構成が作成され、Android エミュレータが自動的に起動します。エミュレータが起動した後、少し経つとアプリケーションが表示されます。次のような画面が表示されます。</p>
+
+ <a href="images/hello_world_5.png"><img src="images/hello_world_5.png" style="height:230px" alt="" /></a>
+
+<p>グレーのバーに表示されている「Hello, Android」は、アプリケーションのタイトルです。このタイトルは Eclipse プラグインによって自動的に作成されます(文字列は <code>res/values/strings.xml</code> ファイル内で定義され、<code>AndroidManifest.xml</code> によって参照されます)。タイトルの下のテキストは、先ほど TextView オブジェクトで作成した実際のテキストです。</p>
+
+<p>これで「Hello World」についての基本的なチュートリアルは終了ですが、この続きもぜひ読んでください。Android アプリケーションの開発に関するさらに有益な情報を紹介しています。</p>
+
+
+<h2 id="upgrading">UI を XML レイアウトにアップグレードする</h2>
+
+<p>先ほど作成した「Hello, World」のサンプルは、「プログラマティック」と呼ばれる UI レイアウトを使用しています。「プログラマティック」とは、アプリケーションの UI を直接ソース コードで作成および構築することを意味します。UI プログラミングの経験が豊富な方であればおそらく、このようなアプローチが時にいかに脆弱になり得るかをよくご存じでしょう。レイアウトの軽微な変更のたびに、ソース コード全体に関わる大きな問題が発生する可能性があるからです。また、複数のビューを適切に結びつけることも忘れられがちであるため、これによりレイアウトにエラーが発生し、コードのデバッグで時間が無駄になる場合があります。</p>
+
+<p>その理由から、Android では、XML ベースのレイアウト ファイルを使用する別の UI 構築モデルを用意しています。この概念を簡単に説明するには、サンプルを紹介するのが一番です。ここに示すのは、上記の「プログラマティック」に構築したサンプルと同じように動作する XML レイアウト ファイルです。</p>
+
+<pre><?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:text="@string/hello"/></pre>
+
+<p>Android XML レイアウト ファイルの全般的な構造はシンプルです。XML 要素がツリー構造になっており、含まれた各ノードが View クラスの名前を表しています(このサンプルでは View 要素が 1 つのみですが)。XML レイアウト内の要素として、{@link android.view.View} を拡張する任意のクラスの名前を使用できます。これには作成するコードの中で定義するカスタム View クラスも含まれます。この構造により、プログラマティックなレイアウトよりもシンプルな構造と構文を使用して、迅速な UI 構築を非常に簡単に行うことができます。このモデルは、アプリケーションの表示(つまり UI)を、データの取得と入力に使用されるアプリケーション ロジックから切り離すことができる Web 開発モデルからヒントを得て考案されました。</p>
+
+<p>上記の XML サンプルには、<code>TextView</code> という View 要素 1 つのみが含まれています。この要素は 4 つの XML 属性を持っています。下表に、これらの 4 つの属性の説明をまとめました。</p>
+
+<table>
+ <tbody>
+ <tr>
+ <th>
+ 属性
+ </th>
+ <th>
+ 説明
+ </th>
+ </tr>
+ <tr>
+ <td>
+ <code>xmlns:android</code>
+ </td>
+ <td>
+ Android ネームスペースで定義された共通の属性を参照することを Android ツールに伝える XML ネームスペース宣言です。すべての Android レイアウト ファイル内の最初と最後のタグはこの属性を持つ必要があります。<br>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>android:layout_width</code>
+ </td>
+ <td>
+ 該当の View が画面の利用可能な幅のうちどれくらいを占めるかを定義します。このサンプルでは、この View しかないため、「fill_parent」という値を使用して画面全体を占めることにします。<br>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>android:layout_height</code>
+ </td>
+ <td>
+ android:layout_width とよく似た属性で、幅ではなく高さを表します。
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>android:text</code>
+ </td>
+ <td>
+ TextView が表示するテキストを設定します。このサンプルでは、ハードコード記述された文字列値ではなく文字列リソースを使用します。文字列「<em>hello</em>」は <em>res/values/strings.xml</em> ファイル内で定義されます。アプリケーションに文字列を挿入する場合にはこの方法が推奨されます。レイアウト ファイルのハードコードを直接変更する必要がないため、アプリケーションの他の言語へのローカライズがスムーズに進むからです。詳しくは、「<a href="{@docRoot}guide/topics/resources/resources-i18n.html">リソースと国際化</a>」を参照してください。
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+<p>これらの XML レイアウト ファイルは、作成するプロジェクトの <code>res/layout/</code> ディレクトリ内に置かれます。「res」は「resources」の略で、アプリケーションに必要なコード以外のすべてのアセットがこのディレクトリに格納されます。リソースには、レイアウト ファイルの他に、画像、音声、ローカライズされた文字列などのアセットがあります。</p>
+
+<div class="sidebox">
+ <h2>横表示レイアウト</h2>
+ <p>横表示の場合に異なるデザインで表示するには、レイアウト XML ファイルを /res/layout-land 内に入れます。Android 端末のレイアウトが横表示に変わると自動的にこのディレクトリが参照されます。このように横表示向けに定義されたレイアウトが存在しない場合、自動的にデフォルトのレイアウトが拡大して使用されます。</p>
+</div>
+
+<p>Eclipse プラグインでは、このようなレイアウト ファイルの 1 つである「main.xml」が自動的に作成されます。先ほど「Hello World」アプリケーションを作成した際には、このファイルは無視してプログラマティックにレイアウトを作成しました。この作成方法は Android フレームワークについてより深く理解していただくことを意図したもので、実際にはほとんどの場合レイアウトはコードではなく XML ファイルで定義します。以下の手順では、既存のアプリケーションを変更して XML レイアウトが使用されるようにする方法を説明します。</p>
+
+<ol>
+ <li>Eclipse のパッケージ エクスプローラー(Package Explorer)で、<code>/res/layout/</code> フォルダを展開し、<code>main.xml</code> を開きます(開いた後、場合によっては XML ソースを見るのにウィンドウ下部にある「main.xml」タブをクリックする必要があります)。ファイルの内容を以下の XML に置き換えます。
+
+<pre><?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:text="@string/hello"/></pre>
+<p>ファイルを保存します。</p>
+</li>
+
+<li><code>res/values/</code> フォルダ内の <code>strings.xml</code> を開きます。このファイルは、作成するユーザー インターフェースのためのすべてのデフォルトのテキスト文字列を保存するものです。Eclipse を使用している場合、ADT によってあらかじめ <em>hello</em> と <em>app_name</em> という 2 つの文字列が用意された状態になります。<em>hello</em> を何か別の文字列に書き換えてみましょう。たとえば「Hello, Android! I am a string resource!」としてみましょう。変更後のファイルの全体は次のようになります。
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="hello">Hello, Android! I am a string resource!</string>
+ <string name="app_name">Hello, Android</string>
+</resources>
+</pre>
+</li>
+
+<li>次に、<code>HelloAndroid</code> クラスを開いて、XML レイアウトを使用して変更します。ファイルを編集して次のような内容にします。
+<pre>
+package com.example.helloandroid;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class HelloAndroid extends Activity {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ }
+}</pre>
+
+<p>この変更を行う際に、コードを手入力してコード補完機能を試してみましょう。「R.layout.main」と入力しようとすると、プラグインによって入力候補が表示されます。この機能の便利さは、開発中に何回も実感すると思います。</p>
+
+<p>View オブジェクトに <code>setContentView()</code> を引き渡す代わりに、レイアウト リソースへの参照を付与します。リソースは <code>R.layout.main</code> として識別されます。これは、<code>/res/layout/main.xml</code> で定義されたレイアウトを、コンパイルされたオブジェクトで表したものです。Eclipse プラグインでは、この参照がプロジェクトの R.java クラス内に自動的に作成されます。Eclipse を使用していない場合、Ant を実行してアプリケーションのビルドを行う際に R.java クラスが生成されます(R クラスについて詳しくは後ほど説明します)。</p>
+</li>
+</ol>
+
+<p>ここで、アプリケーションを再実行します。起動構成は作成済みであるため、ここでは緑色の矢印アイコンをクリックして実行するか、または <strong>[[]実行(Run)] > [[]ヒストリーの実行(Run History)] > [[]Android Activity]</strong> を選択するだけです。TextView 文字列に加えた変更を除けば、アプリケーションは同じに見えます。ここでポイントとなるのは、2 つの異なるレイアウト編集方法を使用して同じ結果が得られるということです。</p>
+
+<p class="note"><strong>ヒント:</strong> ショートカット キー<strong>Ctrl+F11</strong>(Mac では <strong>コマンド+Shift+F11</strong>)を使用して、現在表示されているアプリケーションを実行することができます。</p>
+
+<p>ここからは、デバッグの基礎知識と、他の総合開発環境に関する補足情報について説明します。さらに詳しく学習したい場合は、「<a href="{@docRoot}guide/topics/fundamentals.html">アプリケーションの基礎</a>」を参照してください。Android アプリケーションが動作するためのすべての要素について説明しています。また、「<a href="{@docRoot}guide/index.html">デベロッパー ガイド</a>」の導入ページを参照して、<em>デベロッパー ガイド</em> ドキュメントの概要を確認してください。</p>
+
+
+<div class="special">
+<h3>R クラス</h3>
+<p>Eclipse で、<code>R.java</code> という名前のファイル(<code>gen/</code>(「生成された Java ファイル(Generated Java Files)」フォルダ内)を開きます。ファイルの内容は次のようになっています。</p>
+
+<pre>
+package com.example.helloandroid;
+
+public final class R {
+ public static final class attr {
+ }
+ public static final class drawable {
+ public static final int icon=0x7f020000;
+ }
+ public static final class layout {
+ public static final int main=0x7f030000;
+ }
+ public static final class string {
+ public static final int app_name=0x7f040001;
+ public static final int hello=0x7f040000;
+ }
+}
+</pre>
+
+<p>プロジェクトの <code>R.java</code> ファイルは、ファイル内で定義されたすべてのリソースへのインデックスです。ソース コード内では、プロジェクトに含めたすべてのリソースを参照するための簡略形式としてこのクラスを使用します。これは、Eclipse などの総合開発環境のコード補完機能とともに使用すると特に便利です。探している特定の参照をすばやくインタラクティブに見つけることができるからです。</p>
+
+<p>お手元のファイルはこれとは若干異なる可能性があります(おそらく 16 進値が異なるためです)。ここでは、「layout」という名前の内部クラスと、そのメンバーであるフィールド「main」に注目します。Eclipse プラグインにより main.xml という名前の XML レイアウト ファイルが認識され、ここにそのためのクラスが生成されたものです。プロジェクトに他のリソース(<code>res/values/string.xml</code> ファイル内の文字列や <code>res/drawable/</code> ディレクトリ内の描画可能オブジェクトなど)を追加すると、<code>R.java</code> に最新の変更が反映されます。</p>
+<p>Eclipse を使用していない場合は、(Ant ツールを使用した)ビルド時にこのクラス ファイルが生成されます。</p>
+<p><em>くれぐれもこのファイルを手動で編集しないようにしてください。</em></p>
+</div>
+
+<h2 id="debugging">プロジェクトをデバッグする</h2>
+
+<p>Android Plugin for Eclipse は、Eclipse のデバッガと優れた連動性を発揮します。このメリットを確認するため、作成したコードにバグを埋め込んでみましょう。作成した HelloAndroid ソース コードを次のように変更します。</p>
+
+<pre>
+package com.android.helloandroid;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class HelloAndroid extends Activity {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Object o = null;
+ o.toString();
+ setContentView(R.layout.main);
+ }
+}</pre>
+
+<p>この変更は、単にコードに NullPointerException を発生させるものです。アプリケーションを再度実行すると、最終的に次のような画面が表示されます。</p>
+
+ <a href="images/hello_world_8.png"><img src="images/hello_world_8.png" style="height:230px" alt="" /></a>
+
+<p>「強制終了」を押してアプリケーションを終了し、エミュレータ ウィンドウを閉じます。</p>
+
+<p>エラーの詳細を確認するには、ソース コード内の <code>Object o = null;</code> 行にブレークポイントを設定します(該当するソース コード行の横にあるマーカー バーをダブルクリックします)。次に、メニューから <strong>[[]実行(Run)] > [[]デバッグ ヒストリー(Debug History)] > [[]Hello, Android]</strong> を選択して、デバッグ モードに入ります。エミュレータでアプリケーションが再起動されますが、今度は、先ほど設定したブレークポイントに到達した時点で中断されます。その後 Eclipse のデバッグ パースペクティブ(Debug Perspective)で、他のアプリケーションで通常行うように、コードの内容を確認できます。</p>
+
+ <a href="images/hello_world_9.png"><img src="images/hello_world_9.png" style="height:230px" alt="" /></a>
+
+
+<h2 id="noeclipse">Eclipse を使用せずにプロジェクトを作成する</h2>
+
+ <p>Eclipse を使用していない場合(普段から使用している総合開発環境がある場合や、シンプルにテキスト エディタやコマンド ライン ツールを使用している場合など)は、Eclipse プラグインを利用することはできません。しかし心配は無用です。Eclipse を使用していないからといって何らかの機能が失われることはありません。</p>
+
+ <p>Android Plugin for Eclipse は、単に Android SDK に含まれるツール セットをまとめたものに過ぎません(エミュレータ、aapt、adb、ddms などの個別のツールについては、<a href="{@docRoot}guide/developing/tools/index.html">こちらで別途説明</a>しています)。このため、これらのツールを別のツール、たとえば「Ant」のビルド ファイルなどでまとめることも可能です。</p>
+
+ <p>Android SDK には、「android」という名前のツールが含まれています。このツールを使用すると、作成するプロジェクトのソース コードとディレクトリ スタブすべて、および Ant と互換性のある <code>build.xml</code> ファイルを作成することができます。これにより、プロジェクトをコマンド ラインで作成したり、普段使用している総合開発環境と統合したりすることができます。</p>
+
+ <p>たとえば、Eclipse で作成されるものと同様の HelloAndroid プロジェクトを作成するには、次のコマンドを使用します。</p>
+
+ <pre>
+android create project \
+ --package com.android.helloandroid \
+ --activity HelloAndroid \
+ --target 2 \
+ --path <em><path-to-your-project></em>/HelloAndroid
+</pre>
+
+ <p>これにより、<em>path</em> で定義された場所に、プロジェクトに必要なフォルダとファイルが作成されます。</p>
+
+ <p>SDK ツールを使用してプロジェクトを作成および構築する方法について詳しくは、「<a href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>」を参照してください。</p>
diff --git a/docs/html/intl/ja/sdk/1.5_r2/installing.jd b/docs/html/intl/ja/sdk/1.5_r2/installing.jd
index a587b1a..789fc26 100644
--- a/docs/html/intl/ja/sdk/1.5_r2/installing.jd
+++ b/docs/html/intl/ja/sdk/1.5_r2/installing.jd
@@ -130,7 +130,7 @@
<h4>その他のインストール エラー</h4>
-<p>オプションの Eclipse コンポーネント(WST など)を必要とする ADT の機能があります。ADT のインストール時にエラーが発生した場合、お使いの Eclipse インストール環境にこうしたコンポーネントが含まれていない可能性があります。必要なコンポーネントを Eclipse インストール環境に簡単に追加する方法について詳しくは、トラブルシューティング トピック <a href="{@docRoot}guide/appendix/faq/troubleshooting.html#installeclipsecomponents">ADT インストールエラー: "requires plug-in org.eclipse.wst.sse.ui"</a> をご覧ください。</p>
+<p>オプションの Eclipse コンポーネント(WST など)を必要とする ADT の機能があります。ADT のインストール時にエラーが発生した場合、お使いの Eclipse インストール環境にこうしたコンポーネントが含まれていない可能性があります。必要なコンポーネントを Eclipse インストール環境に簡単に追加する方法について詳しくは、トラブルシューティング トピック <a href="{@docRoot}resources/faq/troubleshooting.html#installeclipsecomponents">ADT インストールエラー: "requires plug-in org.eclipse.wst.sse.ui"</a> をご覧ください。</p>
<h4>Linux ユーザーの場合</h4>
<p>Eclipse 用 ADT プラグインのインストール時に、次のエラーが発生する場合があります。
@@ -166,15 +166,15 @@
<p><strong>サンプル コードの参照</strong></p>
<ul>
- <li><a href="{@docRoot}guide/tutorials/hello-world.html">Hello World アプリケーション</a>を構築します(特に Eclipse ユーザーにおすすめです)。</li>
- <li><a href="{@docRoot}guide/tutorials/notepad/index.html">Notepad チュートリアル</a>に沿って Android アプリケーションを完全に構築します。 </li>
+ <li><a href="{@docRoot}resources/tutorials/hello-world.html">Hello World アプリケーション</a>を構築します(特に Eclipse ユーザーにおすすめです)。</li>
+ <li><a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad チュートリアル</a>に沿って Android アプリケーションを完全に構築します。 </li>
<li><code><em><sdk></em>/platforms/<em><platfrom></em>/samples</code> に収められている他のサンプル アプリケーションのいずれかを新しいプロジェクトとして作成し、自分の開発環境でコンパイルし、実行します。</li>
</ul>
<p><strong>Android デベロッパー グループへのアクセス</strong></p>
<ul>
<li>[<a
- href="{@docRoot}community/index.html">コミュニティ</a>] タブで、Android デベロッパー グループの一覧を参照します。特に <a href="http://groups.google.com/group/android-developers">Android Developers</a> グループは、Android デベロッパー コミュニティがどういうものかを知るのに参考になります。</li>
+ href="{@docRoot}resources/community-groups.html">コミュニティ</a>] タブで、Android デベロッパー グループの一覧を参照します。特に <a href="http://groups.google.com/group/android-developers">Android Developers</a> グループは、Android デベロッパー コミュニティがどういうものかを知るのに参考になります。</li>
</ul>
diff --git a/docs/html/intl/ja/sdk/1.5_r3/installing.jd b/docs/html/intl/ja/sdk/1.5_r3/installing.jd
index a587b1a..789fc26 100644
--- a/docs/html/intl/ja/sdk/1.5_r3/installing.jd
+++ b/docs/html/intl/ja/sdk/1.5_r3/installing.jd
@@ -130,7 +130,7 @@
<h4>その他のインストール エラー</h4>
-<p>オプションの Eclipse コンポーネント(WST など)を必要とする ADT の機能があります。ADT のインストール時にエラーが発生した場合、お使いの Eclipse インストール環境にこうしたコンポーネントが含まれていない可能性があります。必要なコンポーネントを Eclipse インストール環境に簡単に追加する方法について詳しくは、トラブルシューティング トピック <a href="{@docRoot}guide/appendix/faq/troubleshooting.html#installeclipsecomponents">ADT インストールエラー: "requires plug-in org.eclipse.wst.sse.ui"</a> をご覧ください。</p>
+<p>オプションの Eclipse コンポーネント(WST など)を必要とする ADT の機能があります。ADT のインストール時にエラーが発生した場合、お使いの Eclipse インストール環境にこうしたコンポーネントが含まれていない可能性があります。必要なコンポーネントを Eclipse インストール環境に簡単に追加する方法について詳しくは、トラブルシューティング トピック <a href="{@docRoot}resources/faq/troubleshooting.html#installeclipsecomponents">ADT インストールエラー: "requires plug-in org.eclipse.wst.sse.ui"</a> をご覧ください。</p>
<h4>Linux ユーザーの場合</h4>
<p>Eclipse 用 ADT プラグインのインストール時に、次のエラーが発生する場合があります。
@@ -166,15 +166,15 @@
<p><strong>サンプル コードの参照</strong></p>
<ul>
- <li><a href="{@docRoot}guide/tutorials/hello-world.html">Hello World アプリケーション</a>を構築します(特に Eclipse ユーザーにおすすめです)。</li>
- <li><a href="{@docRoot}guide/tutorials/notepad/index.html">Notepad チュートリアル</a>に沿って Android アプリケーションを完全に構築します。 </li>
+ <li><a href="{@docRoot}resources/tutorials/hello-world.html">Hello World アプリケーション</a>を構築します(特に Eclipse ユーザーにおすすめです)。</li>
+ <li><a href="{@docRoot}resources/tutorials/notepad/index.html">Notepad チュートリアル</a>に沿って Android アプリケーションを完全に構築します。 </li>
<li><code><em><sdk></em>/platforms/<em><platfrom></em>/samples</code> に収められている他のサンプル アプリケーションのいずれかを新しいプロジェクトとして作成し、自分の開発環境でコンパイルし、実行します。</li>
</ul>
<p><strong>Android デベロッパー グループへのアクセス</strong></p>
<ul>
<li>[<a
- href="{@docRoot}community/index.html">コミュニティ</a>] タブで、Android デベロッパー グループの一覧を参照します。特に <a href="http://groups.google.com/group/android-developers">Android Developers</a> グループは、Android デベロッパー コミュニティがどういうものかを知るのに参考になります。</li>
+ href="{@docRoot}resources/community-groups.html">コミュニティ</a>] タブで、Android デベロッパー グループの一覧を参照します。特に <a href="http://groups.google.com/group/android-developers">Android Developers</a> グループは、Android デベロッパー コミュニティがどういうものかを知るのに参考になります。</li>
</ul>
diff --git a/docs/html/resources/articles/avoiding-memory-leaks.jd b/docs/html/resources/articles/avoiding-memory-leaks.jd
new file mode 100644
index 0000000..3361bc1
--- /dev/null
+++ b/docs/html/resources/articles/avoiding-memory-leaks.jd
@@ -0,0 +1,109 @@
+page.title=Avoiding Memory Leaks
+@jd:body
+
+
+<p>Android applications are, at least on the T-Mobile G1, limited
+to 16 MB of heap. It's both a lot of memory for a phone and yet very
+little for what some developers want to achieve. Even if you do not
+plan on using all of this memory, you should use as little as possible
+to let other applications run without getting them killed. The more
+applications Android can keep in memory, the faster it will be for the
+user to switch between his apps. As part of my job, I ran into memory
+leaks issues in Android applications and they are most of the time due
+to the same mistake: keeping a long-lived reference to a
+{@link android.content.Context Context}.</p>
+
+<p>On Android, a <code>Context</code> is used for many operations
+ but mostly to load and access resources. This is why all the widgets
+receive a <code>Context</code> parameter in their constructor. In a
+regular Android application, you usually have two kinds of
+<code>Context</code>, {@link android.app.Activity} and
+{@link android.app.Application}. It's usually the first one that
+the developer passes to classes and methods that need a <code>Context</code>:</p>
+
+<pre class="prettyprint">@Override
+protected void onCreate(Bundle state) {
+ super.onCreate(state);
+
+ TextView label = new TextView(this);
+ label.setText("Leaks are bad");
+
+ setContentView(label);
+}
+</pre>
+
+<p>This means that views have a reference to the entire activity and
+therefore to anything your activity is holding onto; usually the entire
+View hierarchy and all its resources. Therefore, if you leak the <code>Context</code>
+("leak" meaning you keep a reference to it thus preventing the GC from
+collecting it), you leak a lot of memory. Leaking an entire activity
+can be really easy if you're not careful.</p>
+
+<p>When the screen orientation changes the system will, by default,
+destroy the current activity and create a new one while preserving its
+state. In doing so, Android will reload the application's UI from the
+resources. Now imagine you wrote an application with a large bitmap
+that you don't want to load on every rotation. The easiest way to keep
+it around and not having to reload it on every rotation is to keep in a
+static field:</p>
+
+<pre class="prettyprint">private static Drawable sBackground;
+
+@Override
+protected void onCreate(Bundle state) {
+ super.onCreate(state);
+
+ TextView label = new TextView(this);
+ label.setText("Leaks are bad");
+
+ if (sBackground == null) {
+ sBackground = getDrawable(R.drawable.large_bitmap);
+ }
+ label.setBackgroundDrawable(sBackground);
+
+ setContentView(label);
+}
+</pre>
+
+<p>This code is very fast and also very wrong; it leaks the first activity
+created upon the first screen orientation change. When a
+{@link android.graphics.drawable.Drawable Drawable} is attached to a view, the view is set as a
+{@link android.graphics.drawable.Drawable#setCallback(android.graphics.drawable.Drawable.Callback) callback}
+on the drawable. In the code snippet above, this means the drawable has a
+reference to the <code>TextView</code> which itself has a reference to the
+activity (the <code>Context</code>) which in turns has references to
+pretty much anything (depending on your code.)</p>
+
+<p>This example is one of the simplest cases of leaking the
+<code>Context</code> and you can see how we worked around it in the
+<a href="http://android.git.kernel.org/?p=platform/packages/apps/Launcher.git;a=blob;f=src/com/android/launcher/LauncherModel.java;h=0ef2a806b767142b28b2ff3b37f21f4ca16c355d;hb=cupcake">Home screen's source code</a>
+(look for the <code>unbindDrawables()</code> method) by setting the stored
+drawables' callbacks to null when the activity is destroyed. Interestingly
+enough, there are cases where you can create a chain of leaked contexts,
+and they are bad. They make you run out of memory rather quickly.</p>
+
+<p>There are two easy ways to avoid context-related memory leaks. The most
+obvious one is to avoid escaping the context outside of its own scope. The
+example above showed the case of a static reference but inner classes and
+their implicit reference to the outer class can be equally dangerous. The
+second solution is to use the <code>Application</code> context. This
+context will live as long as your application is alive and does not depend
+on the activities life cycle. If you plan on keeping long-lived objects
+that need a context, remember the application object. You can obtain it
+easily by calling
+{@link android.content.Context#getApplicationContext() Context.getApplicationContext()}
+or {@link android.app.Activity#getApplication() Activity.getApplication()}.</p>
+
+<p>In summary, to avoid context-related memory leaks, remember the following:</p>
+<ul>
+<li>Do not keep long-lived references to a context-activity (a reference
+to an activity should have the same life cycle as the activity itself)</li>
+<li>Try using the context-application instead of a context-activity</li>
+<li>Avoid non-static inner classes in an activity if you don't control
+their life cycle, use a static inner class and make a weak reference to
+the activity inside. The solution to this issue is to use a static inner
+class with a {@link java.lang.ref.WeakReference WeakReference} to the
+outer class, as done in <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/java/android/view/ViewRoot.java;h=9d7a124cb01ab94bf53e34f6e5e8a07f81e2423c;hb=master">ViewRoot</a>
+and its W inner class for instance</li>
+<li>A garbage collector is not an insurance against memory leaks</li>
+</ul>
diff --git a/docs/html/resources/articles/backward-compatibility.jd b/docs/html/resources/articles/backward-compatibility.jd
new file mode 100644
index 0000000..9f0ddf5
--- /dev/null
+++ b/docs/html/resources/articles/backward-compatibility.jd
@@ -0,0 +1,238 @@
+page.title=Backward Compatibility for Applications
+@jd:body
+
+<p>A variety of Android-powered devices are now available to consumers from carriers
+in geographies around the world. Across those devices, a range of Android
+platform versions are in use, some running the latest version of the platform,
+others running older versions. As a developer, you need to consider the approach
+to backward compatibility that you will take in your application — do you
+want to allow your application to run on all devices, or just those running the
+latest software? In some cases it will be useful to employ the newer APIs on
+devices that support them, while continuing to support older devices. </p>
+
+<h3>Setting the minSdkVersion</h3>
+<p>If the use of a new API is integral to the application — perhaps you
+need to record video using an API introduced in Android 1.5 (API Level 3)
+— you should add a <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><code><android:minSdkVersion></code></a>
+ to the application's manifest, to ensure your app won't
+be installed on older devices. For example, if your application depends on an
+API introduced in API Level 3, you would specify "3" as the value of the minimum
+SDK version</a>:</p>
+
+<pre> <manifest>
+ ...
+ <uses-sdk android:minSdkVersion="3" />
+ ...
+ </manifest></pre>
+
+<p>However, if you want to add a useful but non-essential feature, such as
+popping up an on-screen keyboard even when a hardware keyboard is available, you
+can write your program in a way that allows it to use the newer features without
+failing on older devices.</p>
+
+<h3>Using reflection</h3>
+
+<p>Suppose there's a simple new call you want to use, like {@link
+android.os.Debug#dumpHprofData(java.lang.String)
+android.os.Debug.dumpHprofData(String filename)}. The {@link android.os.Debug}
+class has existed since Android 1.0, but the method is new in Anroid 1.5 (API
+Level 3). If you try to call it directly, your app will fail to run on devices
+running Android 1.1 or earlier.</p>
+
+<p>The simplest way to call the method is through reflection. This requires
+doing a one-time lookup and caching the result in a <code>Method</code> object.
+Using the method is a matter of calling <code>Method.invoke</code> and un-boxing
+the result. Consider the following:</p>
+
+<pre>public class Reflect {
+ private static Method mDebug_dumpHprofData;
+
+ static {
+ initCompatibility();
+ };
+
+ private static void initCompatibility() {
+ try {
+ mDebug_dumpHprofData = Debug.class.getMethod(
+ "dumpHprofData", new Class[] { String.class } );
+ /* success, this is a newer device */
+ } catch (NoSuchMethodException nsme) {
+ /* failure, must be older device */
+ }
+ }
+
+ private static void dumpHprofData(String fileName) throws IOException {
+ try {
+ mDebug_dumpHprofData.invoke(null, fileName);
+ } catch (InvocationTargetException ite) {
+ /* unpack original exception when possible */
+ Throwable cause = ite.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException) cause;
+ } else if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ } else {
+ /* unexpected checked exception; wrap and re-throw */
+ throw new RuntimeException(ite);
+ }
+ } catch (IllegalAccessException ie) {
+ System.err.println("unexpected " + ie);
+ }
+ }
+
+ public void fiddle() {
+ if (mDebug_dumpHprofData != null) {
+ /* feature is supported */
+ try {
+ dumpHprofData("/sdcard/dump.hprof");
+ } catch (IOException ie) {
+ System.err.println("dump failed!");
+ }
+ } else {
+ /* feature not supported, do something else */
+ System.out.println("dump not supported");
+ }
+ }
+}</pre>
+
+<p>This uses a static initializer to call <code>initCompatibility</code>,
+which does the method lookup. If that succeeds, it uses a private
+method with the same semantics as the original (arguments, return
+value, checked exceptions) to do the call. The return value (if it had
+one) and exception are unpacked and returned in a way that mimics the
+original. The <code>fiddle</code> method demonstrates how the
+application logic would choose to call the new API or do something
+different based on the presence of the new method.</p>
+
+<p>For each additional method you want to call, you would add an additional
+private <code>Method</code> field, field initializer, and call wrapper to the
+class.</p>
+
+<p>This approach becomes a bit more complex when the method is declared in a
+previously undefined class. It's also much slower to call
+<code>Method.invoke()</code> than it is to call the method directly. These
+issues can be mitigated by using a wrapper class.</p>
+
+<h3>Using a wrapper class</h3>
+
+<p>The idea is to create a class that wraps all of the new APIs exposed by a new
+or existing class. Each method in the wrapper class just calls through to the
+corresponding real method and returns the same result.</p>
+
+<p>If the target class and method exist, you get the same behavior you would get
+by calling the class directly, with a small amount of overhead from the
+additional method call. If the target class or method doesn't exist, the
+initialization of the wrapper class fails, and your application knows that it
+should avoid using the newer calls.</p>
+
+<p>Suppose this new class were added:</p><pre>public class NewClass {
+ private static int mDiv = 1;
+
+ private int mMult;
+
+ public static void setGlobalDiv(int div) {
+ mDiv = div;
+ }
+
+ public NewClass(int mult) {
+ mMult = mult;
+ }
+
+ public int doStuff(int val) {
+ return (val * mMult) / mDiv;
+ }
+}</pre>
+
+<p>We would create a wrapper class for it:</p>
+
+<pre>class WrapNewClass {
+ private NewClass mInstance;
+
+ /* class initialization fails when this throws an exception */
+ static {
+ try {
+ Class.forName("NewClass");
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /* calling here forces class initialization */
+ public static void checkAvailable() {}
+
+ public static void setGlobalDiv(int div) {
+ NewClass.setGlobalDiv(div);
+ }
+
+ public WrapNewClass(int mult) {
+ mInstance = new NewClass(mult);
+ }
+
+ public int doStuff(int val) {
+ return mInstance.doStuff(val);
+ }
+}</pre>
+
+<p>This has one method for each constructor and method in the original, plus a
+static initializer that tests for the presence of the new class. If the new
+class isn't available, initialization of <code>WrapNewClass</code> fails,
+ensuring that the wrapper class can't be used inadvertently. The
+<code>checkAvailable</code> method is used as a simple way to force class
+initialization. We use it like this:</p>
+
+<pre>public class MyApp {
+ private static boolean mNewClassAvailable;
+
+ /* establish whether the "new" class is available to us */
+ static {
+ try {
+ WrapNewClass.checkAvailable();
+ mNewClassAvailable = true;
+ } catch (Throwable t) {
+ mNewClassAvailable = false;
+ }
+ }
+
+ public void diddle() {
+ if (mNewClassAvailable) {
+ WrapNewClass.setGlobalDiv(4);
+ WrapNewClass wnc = new WrapNewClass(40);
+ System.out.println("newer API is available - " + wnc.doStuff(10));
+ } else {
+ System.out.println("newer API not available");
+ }
+ }
+}</pre>
+
+<p>If the call to <code>checkAvailable</code> succeeds, we know the new class is
+part of the system. If it fails, we know the class isn't there, and adjust our
+expectations accordingly. It should be noted that the call to
+<code>checkAvailable</code> will fail before it even starts if the bytecode
+verifier decides that it doesn't want to accept a class that has references to a
+nonexistent class. The way this code is structured, the end result is the same
+whether the exception comes from the verifier or from the call to
+<code>Class.forName</code>.</p>
+
+<p>When wrapping an existing class that now has new methods, you only need to
+put the new methods in the wrapper class. Invoke the old methods directly. The
+static initializer in <code>WrapNewClass</code> would be augmented to do a
+one-time check with reflection.</p>
+
+<h3>Testing is key</h3>
+
+<p>You must test your application on every version of the Android framework that
+is expected to support it. By definition, the behavior of your application will
+be different on each. Remember the mantra: if you haven't tried it, it doesn't
+work.</p>
+
+<p>You can test for backward compatibility by running your application in an
+emulator that uses an older version of the platform. The Android SDK allows you
+to do this easily by creating "Android Virtual Devices" with different API
+levels. Once you create the AVDs, you can test your application with old and new
+versions of the system, perhaps running them side-by-side to see the
+differences. More information about emulator AVDs can be found <a
+href={@docRoot}guide/developing/tools/avd.html">in the AVD documentation</a> and
+from <code>emulator -help-virtual-device</code>.</p>
\ No newline at end of file
diff --git a/docs/html/resources/articles/can-i-use-this-intent.jd b/docs/html/resources/articles/can-i-use-this-intent.jd
new file mode 100644
index 0000000..a726189
--- /dev/null
+++ b/docs/html/resources/articles/can-i-use-this-intent.jd
@@ -0,0 +1,69 @@
+page.title=Can I Use this Intent?
+@jd:body
+
+<p>Android offers a very powerful and yet easy-to-use message type called
+an <a href="{@docRoot}guide/topics/intents/intents-filters.html">intents</a>.
+You can use intents to turn applications into high-level libraries and
+make code modular and reusable. The Android Home screen and AnyCut
+applications, for instance, use intents extensively to create shortcuts. </p>
+
+<p>While it is nice to be able to make use of a loosely coupled
+API, there is no guarantee that the intent you send will be received by
+another application. This happens in particular with third-party apps, like
+<a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Panoramio">Panoramio</a>
+and its RADAR intent.</p>
+
+<p>This article describes a technique you can use to find out whether the system
+contains any application capable of responding to the intent you want to use.
+The example below shows a helper method that queries the system package manager
+to determine whether there's an app that can respond to a specified intent. Your
+application can pass an intent to the method and then, for example, show or hide
+user options that the user would normally use to trigger the intent. </p>
+
+<pre class="prettyprint">/**
+ * Indicates whether the specified action can be used as an intent. This
+ * method queries the package manager for installed packages that can
+ * respond to an intent with the specified action. If no suitable package is
+ * found, this method returns false.
+ *
+ * @param context The application's environment.
+ * @param action The Intent action to check for availability.
+ *
+ * @return True if an Intent with the specified action can be sent and
+ * responded to, false otherwise.
+ */
+public static boolean isIntentAvailable(Context context, String action) {
+ final PackageManager packageManager = context.getPackageManager();
+ final Intent intent = new Intent(action);
+ List<ResolveInfo> list =
+ packageManager.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return list.size() > 0;
+}
+</pre>
+
+<p>Here is how you could use the helper method:</p>
+
+<pre class="prettyprint">@Override
+public boolean onPrepareOptionsMenu(Menu menu) {
+ final boolean scanAvailable = isIntentAvailable(this,
+ "com.google.zxing.client.android.SCAN");
+
+ MenuItem item;
+ item = menu.findItem(R.id.menu_item_add);
+ item.setEnabled(scanAvailable);
+
+ return super.onPrepareOptionsMenu(menu);
+}
+</pre>
+
+<p>In this example, the menu is grayed out if the <em>Barcode Scanner</em>
+application is not installed. </p>
+
+<p>Another, simpler, way to do this is to catch the
+<code>ActivityNotFoundException</code> when calling <code>startActivity()</code>
+but it only lets you react to the problem, you cannot predict it and update the
+UI accordingly to prevent the user from doing something that won't work. The
+technique described here can also be used at startup time to ask the user
+whether he'd like to install the missing package, you can then simply redirect
+him to the Android Market by using the appropriate URI.</p>
\ No newline at end of file
diff --git a/docs/html/resources/articles/creating-input-method.jd b/docs/html/resources/articles/creating-input-method.jd
new file mode 100644
index 0000000..5a92970
--- /dev/null
+++ b/docs/html/resources/articles/creating-input-method.jd
@@ -0,0 +1,235 @@
+page.title=Creating an Input Method
+@jd:body
+
+
+<p>To create an input method (IME) for entering text into text fields
+and other Views, you need to extend the {@link android.inputmethodservice.InputMethodService}.
+class. This class provides much of the basic implementation for an input
+method, in terms of managing the state and visibility of the input method and
+communicating with the currently visible activity.</p>
+
+<p>A good starting point would be the SoftKeyboard sample code provided as part
+of the SDK. You can modify the sample code to start building your own input
+method.</p>
+
+<p>An input method is packaged like any other application or service. In the
+<code>AndroidManifest.xml</code> file, you declare the input method as a
+service, with the appropriate intent filter and any associated meta data:</p>
+
+<pre><manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.fastinput">
+
+ <application android:label="@string/app_label"><br>
+ <!-- Declares the input method service -->
+ <service android:name="FastInputIME"
+ android:label="@string/fast_input_label"
+ android:permission="android.permission.BIND_INPUT_METHOD">
+ <intent-filter>
+ <action android:name="android.view.InputMethod" />
+ </intent-filter>
+ <meta-data android:name="android.view.im" android:resource="@xml/method" />
+ </service>
+
+ <!-- Optional activities. A good idea to have some user settings. -->
+ <activity android:name="FastInputIMESettings" android:label="@string/fast_input_settings">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest></pre>
+
+<p>If your input method allows the user to tweak some settings, you should
+provide a settings activity that can be launched from the Settings application.
+This is optional and you may choose to provide all user settings directly in
+your IME's UI.</p>
+
+<p>The typical life-cycle of an <code>InputMethodService</code> looks like
+this:</p>
+
+<p><img src="images/ime_003.png" style="border: medium none ; width: 374px; height: 871px;"></p>
+
+<h3>Visual Elements</h3>
+
+<p>There are two main visual elements for an input method—the input view and the
+candidates view. You don't have to follow this style though, if one of them is
+not relevant to your input method experience.</p>
+
+<h4>Input View</h4>
+
+<p>This is where the user can input text either in the form of keypresses,
+handwriting or other gestures. When the input method is displayed for the first
+time, <code>InputMethodService.onCreateInputView()</code> will be called. Create
+and return the view hierarchy that you would like to display in the input method
+window.</p>
+
+<h4>Candidates View</h4>
+
+<p>This is where potential word corrections or completions are presented to the
+user for selection. Again, this may or may not be relevant to your input method
+and you can return <code>null</code> from calls to
+<code>InputMethodService.onCreateCandidatesView()</code>, which is the default
+behavior.</p>
+
+<h3>Designing for the different Input Types</h3>
+
+<p>An application's text fields can have different input types specified on
+them, such as free form text, numeric, URL, email address and search. When you
+implement a new input method, you need to be aware of the different input types.
+Input methods are not automatically switched for different input types and so
+you need to support all types in your IME. However, the IME is not responsible
+for validating the input sent to the application. That's the responsibility of
+the application.</p>
+
+<p>For example, the LatinIME provided with the Android platform provides
+different layouts for text and phone number entry:</p>
+
+<p><img style="margin: 0pt 10px 0pt 0pt; width: 319px; height: 198px;" src="images/ime_002.png"><img style="width: 320px; height: 199px;" src="images/ime.png"></p>
+
+<p><code>InputMethodService.onStartInputView()</code> is called with an<code>
+EditorInfo</code> object that contains details about the input type and other
+attributes of the application's text field.</p><p>(<code>EditorInfo.inputType
+& EditorInfo.TYPE_CLASS_MASK</code>) can be one of many different values,
+including:</p>
+
+<ul>
+<li><code>TYPE_CLASS_NUMBER</code></li>
+<li><code>TYPE_CLASS_DATETIME</code></li>
+<li><code>TYPE_CLASS_PHONE</code></li>
+<li><code>TYPE_CLASS_TEXT</code></li>
+</ul>
+
+<p>See <code>android.text.InputType</code> for more details.</p>
+
+<p><code>EditorInfo.inputType</code> can contain other masked bits that
+indicate the class variation and other flags. For example,
+<code>TYPE_TEXT_VARIATION_PASSWORD</code> or <code>TYPE_TEXT_VARIATION_URI</code>
+or <code>TYPE_TEXT_FLAG_AUTO_COMPLETE</code>.</p>
+
+<h4>Password fields</h4>
+
+<p>Pay
+specific attention when sending text to password fields. Make sure that
+the password is not visible within your UI — neither in the input
+view or the candidates view. Also, do not save the password anywhere without
+explicitly informing the user.</p>
+
+<h3>Landscape vs. portrait</h3>
+
+<p>The UI needs to be able to scale between landscape and portrait orientations.
+In non-fullscreen IME mode, leave sufficient space for the application to show
+the text field and any associated context. Preferably, no more than half the
+screen should be occupied by the IME. In fullscreen IME mode this is not an
+issue.</p>
+
+<h3>Sending text to the application</h3>
+
+<p>There are two ways to send text to the application. You can either send
+individual key events or you can edit the text around the cursor in the
+application's text field.</p>
+
+<p>To send a key event, you can simply construct KeyEvent objects and call
+<code>InputConnection.sendKeyEvent()</code>. Here are some examples:</p>
+
+<pre>InputConnection ic = getCurrentInputConnection();
+long eventTime = SystemClock.uptimeMillis();
+ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
+ KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, 0, 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));
+ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
+ KeyEvent.ACTION_UP, keyEventCode, 0, 0, 0, 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE));</pre>
+
+<p>Or use the convenience method:</p>
+
+<pre>InputMethodService.sendDownUpKeyEvents(keyEventCode);</pre>
+
+<p class="note"><strong>Note</strong>:
+It is recommended to use the above method for certain fields such as
+phone number fields because of filters that may be applied to the text
+after each key press. Return key and delete key should also be sent as
+raw key events for certain input types, as applications may be watching
+for specific key events in order to perform an action.</p>
+
+<p>When editing text in a text field, some of the more useful methods on
+<code>android.view.inputmethod.InputConnection</code> are:</p>
+
+<ul>
+<li><code>getTextBeforeCursor()</code></li>
+<li><code>getTextAfterCursor()</code></li>
+<li><code>deleteSurroundingText()</code></li>
+<li><code>commitText()</code></li>
+</ul>
+
+<p>For example, let's say the text "Fell" is to the left of the cursor
+and you want to replace it with "Hello!":</p>
+
+<pre>InputConnection ic = getCurrentInputConnection();
+ic.deleteSurroundingText(4, 0);
+ic.commitText("Hello", 1);
+ic.commitText("!", 1);</pre>
+
+<h4>Composing text before committing</h4>
+
+<p>If your input method does some kind of text prediction or requires multiple
+steps to compose a word or glyph, you can show the progress in the text field
+until the user commits the word and then you can replace the partial composition
+with the completed text. The text that is being composed will be highlighted in
+the text field in some fashion, such as an underline.</p>
+
+<pre>InputConnection ic = getCurrentInputConnection();
+ic.setComposingText("Composi", 1);
+...
+ic.setComposingText("Composin", 1);
+...
+ic.commitText("Composing ", 1);</pre>
+
+<p><img style="width: 320px; height: 98px; margin-bottom: 10px;" src="images/ime_006.png">
+<img style="width: 320px; height: 97px; margin-bottom: 10px;" src="images/ime_005.png">
+<img style="width: 320px; height: 97px;" src="images/ime_004.png"></p>
+
+<h3>Intercepting hard key events</h3>
+
+<p>Even though the input method window doesn't have explicit focus, it receives
+hard key events first and can choose to consume them or forward them along to
+the application. For instance, you may want to consume the directional keys to
+navigate within your UI for candidate selection during composition. Or you may
+want to trap the back key to dismiss any popups originating from the input
+method window. To intercept hard keys, override
+<code>InputMethodService.onKeyDown()</code> and
+<code>InputMethodService.onKeyUp().</code> Remember to call
+<code>super.onKey</code>* if you don't want to consume a certain key
+yourself.</p>
+
+<h3>Other considerations</h3>
+
+<ul>
+<li>Provide a way for the user to easily bring up any associated settings
+directly from the input method UI</li>
+<li>Provide
+a way for the user to switch to a different input method (multiple
+input methods may be installed) directly from the input method UI.</li>
+<li>Bring
+up the UI quickly - preload or lazy-load any large resources so that
+the user sees the input method quickly on tapping on a text field. And
+cache any resources and views for subsequent invocations of the input
+method.</li>
+<li>On the flip side, any large memory allocations should
+be released soon after the input method window is hidden so that
+applications can have sufficient memory to run. Consider using a
+delayed message to release resources if the input method is in a hidden
+state for a few seconds.</li>
+<li>Make sure that most common characters
+can be entered using the input method, as users may use punctuation in
+passwords or user names and they shouldn't be stuck in a situation
+where they can't enter a certain character in order to gain access into
+a password-locked device.</li>
+</ul>
+
+<h3>Samples</h3>
+
+<p>For a real world example, with support for multiple input types and text
+prediction, see the <a id="ccpb"
+href="http://android.git.kernel.org/?p=platform/packages/inputmethods/LatinIME.
+git;a=tree" title="LatinIME source code online">LatinIME source code</a>. The
+Android SDK also includes a SoftKeyboard sample as well.</p>
diff --git a/docs/html/resources/articles/drawable-mutations.jd b/docs/html/resources/articles/drawable-mutations.jd
new file mode 100644
index 0000000..f979829
--- /dev/null
+++ b/docs/html/resources/articles/drawable-mutations.jd
@@ -0,0 +1,91 @@
+page.title=Drawable Mutations
+@jd:body
+
+<p>Android's drawables are extremely useful to easily build applications. A
+{@link android.graphics.drawable.Drawable Drawable} is a pluggable drawing
+container that is usually associated with a View. For instance, a
+{@link android.graphics.drawable.BitmapDrawable BitmapDrawable} is used to display
+images, a {@link android.graphics.drawable.ShapeDrawable ShapeDrawable} to draw
+shapes and gradients, and so on. You can even combine them to create complex
+renderings.</p>
+
+<p>Drawables allow you to easily customize the rendering of the widgets without
+subclassing them. As a matter of fact, they are so convenient that most of the
+default Android apps and widgets are built using drawables; there are about 700
+drawables used in the core Android framework. Because drawables are used so
+extensively throughout the system, Android optimizes them when they are loaded
+from resources. For instance, every time you create a
+{@link android.widget.Button Button}, a new drawable is loaded from the framework
+resources (<code>android.R.drawable.btn_default</code>). This means all buttons
+across all the apps use a different drawable instance as their background.
+However, all these drawables share a common state, called the "constant state."
+The content of this state varies according to the type of drawable you are
+using, but it usually contains all the properties that can be defined by a
+resource. In the case of a button, the constant state contains a bitmap image.
+This way, all buttons across all applications share the same bitmap, which saves
+a lot of memory.</p>
+
+<p>The following diagram shows what entities are
+created when you assign the same image resource as the background of
+two different views. As you can see, two drawables are created but they
+both share the same constant state, hence the same bitmap:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 307px; height: 400px;" src="images/shared_states.png" alt="" id="BLOGGER_PHOTO_ID_5331437883277472082" border="0">
+
+<p>This state sharing feature is great to avoid wasting memory but it can cause
+problems when you try to modify the properties of a drawable. Imagine an
+application with a list of books. Each book has a star next to its name, totally
+opaque when the user marks the book as a favorite, and translucent when the book
+is not a favorite. To achieve this effect, you would probably write the
+following code in your list adapter's <code>getView()</code> method:</p>
+
+<pre>Book book = ...;
+TextView listItem = ...;
+
+listItem.setText(book.getTitle());
+
+Drawable star = context.getResources().getDrawable(R.drawable.star);
+if (book.isFavorite()) {
+ star.setAlpha(255); // opaque
+} else {
+ star.setAlpha(70); // translucent
+}</pre>
+
+<p>Unfortunately, this piece of code yields a rather strange result:
+all of the drawables have the same opacity:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 267px; height: 400px;" src="images/all_drawables_changed.png" alt="" id="BLOGGER_PHOTO_ID_5331438978390342066" border="0">
+
+<p>This
+result is explained by the constant state. Even though we are getting a
+new drawable instance for each list item, the constant state remains
+the same and, in the case of BitmapDrawable, the opacity is part of the
+constant state. Thus, changing the opacity of one drawable instance
+changes the opacity of all the other instances. Even worse, working
+around this issue was not easy with Android 1.0 and 1.1.</p>
+
+<p>Android 1.5 and higher offers a very easy way to solve this issue
+with the new {@link android.graphics.drawable.Drawable#mutate()} method</a>.
+When you invoke this method on a drawable, the constant state of the
+drawable is duplicated to allow you to change any property without
+affecting other drawables. Note that bitmaps are still shared, even
+after mutating a drawable. The diagram below shows what happens when
+you invoke <code>mutate()</code> on a drawable:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 307px; height: 400px;" src="images/mutated_states.png" alt="" id="BLOGGER_PHOTO_ID_5331440144116345074" border="0">
+
+<p>Let's update our previous piece of code to make use of <code>mutate()</code>:</p>
+
+<pre>Drawable star = context.getResources().getDrawable(R.drawable.star);
+if (book.isFavorite()) {
+ star.mutate().setAlpha(255); // opaque
+} else {
+ star. mutate().setAlpha(70); // translucent
+}</pre>
+
+<p>For convenience, <code>mutate()</code>
+returns the drawable itself, which allows to chain method calls. It
+does not however create a new drawable instance. With this new piece of
+code, our application now behaves correctly:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 267px; height: 400px;" src="images/correct_drawables.png" alt="" id="BLOGGER_PHOTO_ID_5331440757515573842" border="0">
diff --git a/docs/html/resources/articles/faster-screen-orientation-change.jd b/docs/html/resources/articles/faster-screen-orientation-change.jd
new file mode 100644
index 0000000..c500035
--- /dev/null
+++ b/docs/html/resources/articles/faster-screen-orientation-change.jd
@@ -0,0 +1,115 @@
+page.title=Faster Screen Orientation Change
+@jd:body
+
+<p>Android is designed to run efficiently on a wide
+array of devices, with very different hardware configurations. Some
+devices, like the T-Mobile G1, can change their hardware configuration
+at runtime. For instance, when you open the keyboard, the screen change
+from the portrait orientation to the landscape orientation.
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Using the alternate resources framework</h2>
+
+<p>The platform's support for loading orientation-specific
+resources at run time is based on the alternate resources framework.</p>
+
+<p>Providing orientation-specific resources is an important part of
+developing your app. If you are not familiar with resource directory qualifiers
+or how the platform uses them, please read
+<a href="{@docRoot}guide/topics/resources/resources-i18n.html#AlternateResources">
+Alternate Resources</a>.
+</div>
+</div>
+
+<p>To make
+Android app development easier, the Android system automatically handles
+configuration change events and restarts the current activity with the new
+configuration. This is the default behavior that lets you declare
+resources like layouts and drawables based on the orientation, screen
+size, locale, etc. </p>
+
+<p>While this behavior is really powerful, since your application adapts
+automatically to the device's configuration at runtime, it is sometimes
+confusing for new Android developers, who wonder why their activity is
+destroyed and recreated. </p>
+
+<p>Facing this "issue," some developers choose to handle configuration changes
+themselves which is, in general, a short-term solution that will only complicate
+their lives later. On the other hand, the system's automatic resource handling
+is a very efficient and easy way to adapt an application's user interface to
+various devices and devices configurations. It sometimes comes at a price,
+though.</p>
+
+<p>When your application displays a lot of data, or data that is expensive to fetch,
+the automatic destruction/creation of the activities can be lead to a
+painful user experience. Take the example of <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/">Photostream</a>,
+a simple Flickr browsing application. After you launch the application and choose a Flickr account, the
+application downloads a set of 6 photos (on a T-Mobile G1) from the
+Flickr servers and displays them on screen. To improve the user
+experience, the application uses slightly different layouts and drawables in
+portrait and landscape modes and this is what the result looks like:</p>
+
+<p><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_9l0GmPwgCzk/SZoGyJyg6-I/AAAAAAAAACU/ItuVwhegPb8/s1600-h/photostream_landscape.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 214px;" src="images/photostream_landscape.png" alt="" id="BLOGGER_PHOTO_ID_5303558969873198050" border="0"></a></p>
+
+<p><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_9l0GmPwgCzk/SZoGx4I-QlI/AAAAAAAAACM/-GkZR5MUKhY/s1600-h/photostream_portrait.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 214px; height: 320px;" src="images/photostream_portrait.png" alt="" id="BLOGGER_PHOTO_ID_5303558965135557202" border="0"></a></p>
+
+<p>Photostream lets Android take care of the configuration change when the
+screen is rotated. However, can you imagine how painful it would be for the user
+to see all the images being downloaded again? The obvious solution to this
+problem is to temporarily cache the images. They could be cached on the SD card
+(if there's one), in the Application object, in a static field, etc. None of
+these techniques is adapted to the current situation: why should we bother
+caching the images when the screen is not rotated? Fortunately for us, Android
+offers a great API exactly for that purpose.</p>
+
+<p>The Activity class has a special method called
+{@link android.app.Activity#onRetainNonConfigurationInstance()}. This method
+can be used to pass an arbitrary object <em>your future self</em> and Android
+is smart enough to call this method only when needed. In the case of Photostream,
+the application <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/PhotostreamActivity.java#226">used this method</a>
+to pass the downloaded images to the future activity on orientation change.
+The implementation can be summarized like so:</p>
+
+<pre class="prettyprint">@Override
+public Object onRetainNonConfigurationInstance() {
+ final LoadedPhoto[] list = new LoadedPhoto[numberOfPhotos];
+ keepPhotos(list);
+ return list;
+}
+</pre>
+
+<p>In the new activity, in <code>onCreate()</code>, all you have to do to
+get your object back is to call {@link android.app.Activity#getLastNonConfigurationInstance()}.
+In Photostream, <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/PhotostreamActivity.java#251">this method is invoked</a>
+and if the returned value is not null, the grid is loaded with the list of
+photos from the previous activity:</p>
+
+<pre class="prettyprint">private void loadPhotos() {
+ final Object data = getLastNonConfigurationInstance();
+
+ // The activity is starting for the first time, load the photos from Flickr
+ if (data == null) {
+ mTask = new GetPhotoListTask().execute(mCurrentPage);
+ } else {
+ // The activity was destroyed/created automatically, populate the grid
+ // of photos with the images loaded by the previous activity
+ final LoadedPhoto[] photos = (LoadedPhoto[]) data;
+ for (LoadedPhoto photo : photos) {
+ addPhoto(photo);
+ }
+ }
+}
+</pre>
+
+<p>Be very careful with the object you pass through
+<code>onRetainNonConfigurationChange()</code>, though. If the object you
+pass is for some reason tied to the Activity/Context, <a
+href="http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/">you will leak</a>
+all the views and resources of the activity. This means you should
+never pass a View, a Drawable, an Adapter, etc. Photostream for
+instance extracts the bitmaps from the drawables and pass the bitmaps
+only, not the drawables. Finally, remember that
+<code>onRetainNonConfigurationChange()</code> should be used only to retain
+data that is expensive to load. Otherwise, keep it simple and let Android
+do everything.</p>
diff --git a/docs/html/resources/articles/future-proofing.jd b/docs/html/resources/articles/future-proofing.jd
new file mode 100644
index 0000000..ee98186
--- /dev/null
+++ b/docs/html/resources/articles/future-proofing.jd
@@ -0,0 +1,89 @@
+page.title=Future-Proofing Your Apps
+@jd:body
+
+<p>It's important to implement your application so that it will not break as new
+versions of the Android platform are loaded onto the users device. The list
+below is based on our observations of five ways that we've seen bad apps fail.
+You can think of these as "anti-patterns" (that is, techniques to avoid) for
+Android development.</p>
+
+<p>If your application uses any of the dubious techniques below, break out
+your IDE and duct tape, spackle, and patch up the app.</p>
+
+<p><b>Technique to Avoid, #1: Using Internal APIs</b></p>
+
+<p>Even
+though we've always strongly advised against doing so, some developers
+have chosen to use unsupported or internal APIs. For instance, many
+developers are using the internal brightness control and bluetooth
+toggle APIs that were present in 1.0 and 1.1. A bug -- which was
+fixed in Android 1.5 -- allowed apps to use those APIs without
+requesting permission. As a result, apps that used those APIs broke
+on 1.5. If you've used internal APIs in your apps, you need to update
+your apps to stop doing so. </p>
+
+<p><b>Technique to Avoid, #2: Directly Manipulating Settings</b></p>
+
+<p>Strictly speaking this one isn't evil, since this is a change in
+behavior that we made to Android itself. But we made it because some
+developers were doing naughty things: a number of apps were changing
+system settings silently without even notifying the user. For instance,
+some apps turn on GPS without asking the user, and others might turn on
+data roaming.</p>
+
+<p>As a result, applications can no longer directly
+manipulate the values of certain system Settings, even if they
+previously had permission to do so. For instance, apps can no longer
+directly turn on or off GPS. These apps won't crash, but the APIs in
+question now have no effect, and do nothing. Instead, apps will need to
+issue an Intent to launch the appropriate Settings configuration
+screen, so that the user can change these settings manually. For
+details, see the android.provider.Settings.Secure class, which you can
+find in the 1.5_pre SDK documentation (and later). Note that only
+Settings that were moved to the Settings.Secure class are affected.
+Other, less sensitive, settings will continue to have the same behavior
+as in Android 1.1.</p>
+
+<p><b>Technique to Avoid, #3: Going Overboard with Layouts</b></p>
+
+<p>Due to changes in the View rendering infrastructure, unreasonably deep
+(more than 10 or so) or broad (more than 30 total) View hierarchies in
+layouts are now likely to cause crashes. This was always a risk for
+excessively complex layouts, but you can think of Android 1.5 as being
+better than 1.1 at exposing this problem. Most developers won't need to
+worry about this, but if your app has very complicated layouts, you'll
+need to put it on a diet. You can simplify your layouts using the more
+advanced layout classes like FrameLayout and TableLayout.</p>
+
+<p><b>Technique to Avoid, #4: Bad Hardware Assumptions</b></p>
+
+<p>Android 1.5 includes support for soft keyboards, and there will soon be many
+devices that run Android but do not have physical keyboards. If your
+application assumes the presence of a physical keyboard (such as if you
+have created a custom View that sinks keypress events) you should make
+sure it degrades gracefully on devices that only have soft keyboards.
+For more information on this, keep on eye on this blog as we'll be
+posting more detailed information about handling the new soft keyboards.</p>
+
+<p><b>Technique to Avoid, #5: Incautious Rotations </b></p>
+
+<p>Devices running Android 1.5 and later can automatically rotate the screen,
+depending on how the user orients the device. Some 1.5 devices will do
+this by default, and on all others it can be turned on by the user.
+This can sometimes result in unpredictable behavior from applications
+that do their own reorientations (whether using the accelerometer, or
+something else.) This often happens when applications assume that the
+screen can only rotate if the physical keyboard is exposed; if the
+device lacks a physical keyboard, these apps do not expect to be
+reoriented, which is a coding error. Developers should be sure that
+their applications can gracefully handle being reoriented at any time.</p>
+
+<p>Also, apps that use the accelerometer directly to reorient themselves
+sometimes compete with the system doing the same thing, with odd
+results. And finally, some apps that use the accelerometer to detect
+things like shaking motions and that don't lock their orientation to
+portrait or landscape, often end up flipping back and forth between
+orientations. This can be irritating to the user. (You can lock your
+app's orientation to portrait or landscape using the
+<code>android:screenOrientation</code> attribute in the manifest file.)</p>
+
diff --git a/docs/html/resources/articles/gestures.jd b/docs/html/resources/articles/gestures.jd
new file mode 100644
index 0000000..8711645
--- /dev/null
+++ b/docs/html/resources/articles/gestures.jd
@@ -0,0 +1,211 @@
+page.title=Gestures
+@jd:body
+
+<p>Touch screens are a great way to interact with applications on
+mobile devices. With a touch screen, users can easily tap, drag, fling,
+or slide to quickly perform actions in their favorite applications.
+For app developers. the Android framework makes it's easy to
+recognize simple actions, like a swipe, but it has been more
+difficult to handle complicated gestures, sometimes requiring
+developers to write a lot of code.</p>
+
+<p>That's why we introduced a new gestures API in Android 1.6. This API, located
+in the new package {@link android.gesture}, lets you store, load, draw, and
+recognize gestures. This article will show you how you can use the
+<code>android.gesture</code> API in your applications. Before going any further,
+you should <a
+href="http://code.google.com/p/apps-for-android/downloads/detail?name=
+GesturesDemos.zip&can=2&q=#makechanges">download the source code
+of the examples</a>.</p>
+
+<h3>Creating a gestures library</h3>
+
+<p>Android 1.6 and higher SDK platforms include a new application pre-installed
+on the emulator, called Gestures Builder. You can use this application to create
+a set of pre-defined gestures for your own application. It also serves as an
+example of how to let the user define his own gestures in your applications. You
+can find the source code of Gestures Builders in the samples directory of each
+SDK platform. In our example we will use Gestures Builder to generate a set of
+gestures for us (make sure to create an AVD with an SD card image to use
+Gestures Builder.) The screenshot below shows what the application looks like
+after adding a few gestures:</p>
+
+<img src="images/gestures_006.png" style="width: 320px; height: 480px;">
+
+<p>As you can see, a gesture is always associated with a name. That name is very
+important because it identifies each gesture within your application. The names
+do not have to be unique. Actually it can be very useful to have several
+gestures with the same name to increase the precision of the recognition. Every
+time you add or edit a gesture in the Gestures Builder, a file is generated on
+the emulator's SD card, <code>/sdcard/gestures</code>. This file contains the
+description of all the gestures, and you will need to package it inside your
+application inside the resources directory, in
+<code>/res/raw</code>.</p>
+
+<h3>Loading the gestures library</h3>
+
+<p>Now that you have a set of pre-defined gestures, you must load it inside your
+application. This can be achieved in several ways but the easiest is to use the
+<code>GestureLibraries</code> class:</p>
+
+<pre class="prettyprint">mLibrary = GestureLibraries.fromRawResource(this, R.raw.spells);
+if (!mLibrary.load()) {
+ finish();
+}</pre>
+
+<p>In this example, the gesture library is loaded from the file
+<code>/res/raw/spells</code>. You can easily load libraries from other sources,
+like the SD card, which is very important if you want your application to be
+able to save the library; a library loaded from a raw resource is read-only and
+cannot be modified. The following diagram shows the structure of a library:</p>
+
+<img src="images/gestures_002.png" style="width: 600px; height: 512px;">
+
+<h3>Recognizing gestures</h3>
+
+<p>To start recognizing gestures in your application, all you have to do
+is add a <code>GestureOverlayView</code> to your XML layout:</p>
+
+<pre><android.gesture.GestureOverlayView
+ android:id="@+id/gestures"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1.0" /></pre>
+
+<p>Notice that the <code>GestureOverlayView</code>
+is not part of the usual android.widget package. Therefore, you must
+use its fully qualified name. A gesture overlay acts as a simple
+drawing board on which the user can draw his gestures. You can tweak
+several visual properties, like the color and the width of the stroke
+used to draw gestures, and register various listeners to follow what
+the user is doing. The most commonly used listener is
+<code>GestureOverlayView.OnGesturePerformedListener</code>,
+which fires whenever a user is done drawing a gesture:</p>
+
+<pre>GestureOverlayView gestures = (GestureOverlayView) findViewById(R.id.gestures);
+gestures.addOnGesturePerformedListener(this);</pre>
+
+<p>When the listener fires, you can ask the <code>GestureLibrary</code>
+to try to recognize the gesture. In return, you will get a list of
+Prediction instances, each with a name - the same name you entered in
+the Gestures Builder - and a score. The list is sorted by descending
+scores; the higher the score, the more likely the associated gesture is
+the one the user intended to draw. The following code snippet
+demonstrates how to retrieve the name of the first prediction:</p>
+
+<pre>public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
+ ArrayList<prediction> predictions = mLibrary.recognize(gesture);
+
+ // We want at least one prediction
+ if (predictions.size() > 0) {
+ Prediction prediction = predictions.get(0);
+ // We want at least some confidence in the result
+ if (prediction.score > 1.0) {
+ // Show the spell
+ Toast.makeText(this, prediction.name, Toast.LENGTH_SHORT).show();
+ }
+ }
+}</pre>
+
+<p>In this example, the first prediction is taken into account only if it's
+score is greater than 1.0. The threshold you use is entirely up to you
+but know that scores lower than 1.0 are typically poor matches. And
+this is all the code you need to create a simple application that can
+recognize pre-defined gestures (see the source code of the project
+GesturesDemo):</p>
+
+<img src="images/gestures.png" style="width: 320px; height: 480px;">
+
+<h3>Gestures overlay</h3>
+
+<p>In the example above, the <code>GestureOverlayView</code> was used
+as a normal view, embedded inside a <code>LinearLayout</code>.
+However, as its name suggests, it can also be used as an overlay on top
+of other views. This can be useful to recognize gestures in a game or
+just anywhere in the UI of an application. In the second example,
+called GesturesListDemo, we'll create an overlay on top of a list of
+contacts. We start again in Gestures Builder to create a new set of
+pre-defined gestures:</p>
+
+<p><img src="images/gestures_005.png" style="width: 320px; height: 480px;"></p>
+
+<p>And here is what the XML layout looks like:</p>
+
+<pre><android.gesture.GestureOverlayView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/gestures"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+
+ android:gestureStrokeType="multiple"
+ android:eventsInterceptionEnabled="true"
+ android:orientation="vertical">
+
+ <ListView
+ android:id="@android:id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" />
+
+</android.gesture.GestureOverlayView></pre>
+
+<p>In this application, the gestures view is an overlay on top of a regular
+ListView. The overlay also specifies a few properties that we did not
+need before:</p>
+
+<ul>
+<li><code>gestureStrokeType</code>: indicates
+whether we want to recognize gestures made of a single stroke or
+multiple strokes. Since one of our gestures is the "+" symbol, we need
+multiple strokes</li>
+<li><code>eventsInterceptionEnabled</code>: when
+set to true, this property tells the overlay to steal the events from
+its children as soon as it knows the user is really drawing a gesture.
+This is useful when there's a scrollable view under the overlay, to
+avoid scrolling the underlying child as the user draws his gesture </li>
+<li><code>orientation</code>:
+indicates the scroll orientation of the views underneath. In this case
+the list scrolls vertically, which means that any horizontal gestures
+(like <code>action_delete</code>) can immediately be recognized as a
+gesture. Gestures that start with a vertical stroke must contain at
+least one horizontal component to be recognized. In other words, a
+simple vertical line cannot be recognized as a gesture since it would
+conflict with the list's scrolling.</li>
+</ul>
+
+<p>The code used to load and set up the gestures library and overlay is exactly
+the same as before. The only difference is that we now check the name of the
+predictions to know what the user intended to do:</p>
+
+<pre>public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
+ ArrayList<Prediction> predictions = mLibrary.recognize(gesture);
+ if (predictions.size() > 0 && predictions.get(0).score > 1.0) {
+ String action = predictions.get(0).name;
+ if ("action_add".equals(action)) {
+ Toast.makeText(this, "Adding a contact", Toast.LENGTH_SHORT).show();
+ } else if ("action_delete".equals(action)) {
+ Toast.makeText(this, "Removing a contact", Toast.LENGTH_SHORT).show();
+ } else if ("action_refresh".equals(action)) {
+ Toast.makeText(this, "Reloading contacts", Toast.LENGTH_SHORT).show();
+ }
+ }
+}</pre>
+
+<p>The user is now able to draw his gestures on top of the list without
+interfering with the scrolling:</p>
+
+<img src="images/gestures_004.png" style="width: 320px; height: 480px;">
+
+<p>The overlay even gives visual clues as to whether the gesture is considered
+valid for recognition. In the case of a vertical overlay, for instance,
+a single vertical stroke cannot be recognized as a gesture and is
+therefore drawn with a translucent color:</p>
+
+<img src="images/gestures_003.png" style="width: 320px; height: 480px;">
+
+<h3>It's your turn</h3>
+
+<p>Adding support for gestures in your application is easy and can be a valuable
+addition. The gestures API does not even have to be used to recognize complex
+shapes; it will work equally well to recognize simple swipes. We are very
+excited by the possibilities the gestures API offers, and we're eager to see
+what cool applications the community will create with it.</p>
diff --git a/docs/html/resources/articles/glsurfaceview.jd b/docs/html/resources/articles/glsurfaceview.jd
new file mode 100644
index 0000000..57403ea
--- /dev/null
+++ b/docs/html/resources/articles/glsurfaceview.jd
@@ -0,0 +1,268 @@
+page.title=Introducing GLSurfaceView
+@jd:body
+
+
+<p>The {@link android android.opengl.GLSurfaceView} class makes it
+easier for you to use OpenGL ES rendering in your applications by:</p>
+
+<ul>
+<li>Providing the glue code to connect OpenGL ES to the {@link
+android.view.View} system.</li>
+<li>Providing the glue code to make OpenGL ES work with the {@link
+android.app.Activity} life-cycle.</li>
+<li>Making it easy to choose an appropriate frame buffer pixel format.</li>
+<li>Creating and managing a separate rendering thread, to enable smooth
+animation.</li>
+<li>Providing easy-to-use debugging tools for tracing OpenGL ES API calls and
+checking for errors.</li>
+</ul>
+
+<p>GLSurfaceView is a good base for building an application that uses OpenGL ES
+for part or all of its rendering. A 2D or 3D action game would be a good
+candidate, as would a 2D or 3D data visualization application such as <a
+href="http://www.youtube.com/watch?v=4PRfVKzuUJ4&fmt=18" title="Google Maps
+StreetView">Google Maps StreetView</a>.</p>
+
+<h3>A simple GLSurfaceView application</h3>
+
+<p>Here's the source code to the simplest possible OpenGL ES application:</p>
+
+<pre>package com.example.android.apis.graphics;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.app.Activity;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+
+public class ClearActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mGLView = new GLSurfaceView(this);
+ mGLView.setRenderer(new ClearRenderer());
+ setContentView(mGLView);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mGLView.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mGLView.onResume();
+ }
+
+ private GLSurfaceView mGLView;
+}
+
+class ClearRenderer implements GLSurfaceView.Renderer {
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ // Do nothing special.
+ }
+
+ public void onSurfaceChanged(GL10 gl, int w, int h) {
+ gl.glViewport(0, 0, w, h);
+ }
+
+ public void onDrawFrame(GL10 gl) {
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ }
+}</pre>
+
+<p>This program doesn't do much: it clears the screen to black on every frame.
+But it is a complete OpenGL application that correctly implements the
+Android activity life-cycle. It pauses rendering when the activity is
+paused, and resumes it when the activity is resumed. You could use this
+application as the basis for non-interactive demonstration programs.
+Just add more OpenGL calls to the <code>ClearRenderer.onDrawFrame()</code> method.
+Notice that you don't even need to subclass the <code>GLSurfaceView</code> view.</p>
+
+<p>The {@link android.opengl.GLSurfaceView.Renderer} interface has three methods:</p>
+
+<ul>
+<li>The
+<code>onSurfaceCreated()</code> method is called at the start of rendering, and
+whenever the OpenGL ES drawing context has to be recreated. (The
+drawing context is typically lost and recreated when the activity is
+paused and resumed.) <code>OnSurfaceCreated()</code> is a good place to create
+long-lived OpenGL resources such as textures.</li>
+<li>The <code>onSurfaceChanged()</code>
+method is called when the surface changes size. It's a good place to
+set your OpenGL viewport. You may also want to set your camera here, if
+it's a fixed camera that doesn't move around the scene.</li>
+<li>The <code>onDrawFrame()</code> method is called every frame, and is
+responsible for drawing the scene. You would typically start by calling
+<code>glClear</code> to clear the framebuffer, followed by other OpenGL ES calls
+to draw the current scene.</li>
+</ul>
+
+<h3>How about user input?</h3>
+
+<p>If you want an interactive application (such as a game), you will typically
+subclass <code>GLSurfaceView</code>, because that's an easy way of obtaining
+input events. Here's a slightly longer example showing how to do that:</p>
+
+<pre>package com.google.android.ClearTest;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.app.Activity;
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.MotionEvent;
+
+public class ClearActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mGLView = new ClearGLSurfaceView(this);
+ setContentView(mGLView);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mGLView.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mGLView.onResume();
+ }
+
+ private GLSurfaceView mGLView;
+}
+
+class ClearGLSurfaceView extends GLSurfaceView {
+ public ClearGLSurfaceView(Context context) {
+ super(context);
+ mRenderer = new ClearRenderer();
+ setRenderer(mRenderer);
+ }
+
+ public boolean onTouchEvent(final MotionEvent event) {
+ queueEvent(new Runnable(){
+ public void run() {
+ mRenderer.setColor(event.getX() / getWidth(),
+ event.getY() / getHeight(), 1.0f);
+ }});
+ return true;
+ }
+
+ ClearRenderer mRenderer;
+}
+
+class ClearRenderer implements GLSurfaceView.Renderer {
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ // Do nothing special.
+ }
+
+ public void onSurfaceChanged(GL10 gl, int w, int h) {
+ gl.glViewport(0, 0, w, h);
+ }
+
+ public void onDrawFrame(GL10 gl) {
+ gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ }
+
+ public void setColor(float r, float g, float b) {
+ mRed = r;
+ mGreen = g;
+ mBlue = b;
+ }
+
+ private float mRed;
+ private float mGreen;
+ private float mBlue;
+}</pre>
+
+<p>This application clears the screen for every frame. When you tap on the
+screen, it sets the clear color based on the (x,y) coordinates of your touch
+event. Note the use of <code>queueEvent()</code> in
+<code>ClearGLSurfaceView.onTouchEvent()</code>. The <code>queueEvent()</code>
+method is used to safely communicate between the UI thread and the rendering
+thread. If you prefer, you can use some other Java cross-thread communication
+technique, such as synchronized methods on the <code>Renderer</code> class
+itself. However, queueing events is often the simplest way of dealing with
+cross-thread communication.</p>
+
+<h3>Other GLSurfaceView samples</h3>
+
+<p>Tired
+of just clearing the screen? You can find more interesting samples in
+the API Demos sample included in the Android SDK. All the OpenGL ES samples have been
+converted to use the <code>GLSurfaceView</code> view:</p>
+
+<ul>
+<li>GLSurfaceView - a spinning triangle</li>
+<li>Kube - a cube puzzle demo</li>
+<li>Translucent GLSurfaceView - shows how to display 3D graphics on a translucent background</li>
+<li>Textured Triangle - shows how to draw a textured 3D triangle</li>
+<li>Sprite Text - shows how to draw text into a texture and then composite it into a 3D scene</li>
+<li>Touch Rotate - shows how to rotate a 3D object in response to user input.</li>
+</ul>
+
+<h3>Choosing a surface</h3>
+
+<p><code>GLSurfaceView</code>
+helps you choose the type of surface to render to. Different Android
+devices support different types of surfaces, with no common subset.
+This makes it tricky problem to choose the best available surface on
+each device. </p>
+
+<p>By default, <code>GLSurfaceView</code> tries to find a surface that's as
+close as possible to a 16-bit RGB frame buffer with a 16-bit depth
+buffer. Depending upon your application's needs you may want to change
+this behavior. For example, the Translucent GLSurfaceView sample needs
+an Alpha channel in order to render translucent data. <code>GLSurfaceView</code>
+provides an overloaded <code>setEGLSurfaceChooser()</code> method to give
+you control over which surface type is chosen:</p>
+
+<dl>
+<dt><code>setEGLConfigChooser(boolean needDepth)</code></dt>
+<dd>Choose a config that's closest to R5G6B5 with or without a 16-bit framebuffer</dd>
+<dt><code>setEGLConfigChooser(int redSize, int greenSize,int blueSize,
+int alphaSize,int depthSize, int stencilSize)</code></dt>
+<dd>Choose the config with the fewest number of bits per pixel that has at least
+as many bits-per-channel as specified in the constructor.</dd>
+<dt><code>setEGLConfigChooser(EGLConfigChooser configChooser)</code></dt>
+<dd>Allow total control over choosing a configuration. You pass in your own
+implementation of <code>EGLConfigChooser</code>, which gets to inspect the
+device's capabilities and choose a configuration.</dd>
+</dl>
+
+<h3>Continuous rendering versus render-when-dirty</h3>
+
+<p>Most 3D applications, such as games or simulations, are continuously
+animated. But some 3D applications are more reactive: they wait passively until
+the user does something, and then react to it. For those types of applications,
+the default <code>GLSurfaceView</code> behavior of continuously redrawing the
+screen is a waste of time. If you are developing a reactive application, you can
+call <code>GLSurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY)</code>, which
+turns off the continuous animation. Then you call
+<code>GLSurfaceView.requestRender()</code> whenever you want to re-render.</p>
+
+<h3>Help With debugging</h3>
+
+<p><code>GLSurfaceView</code> has a handy built-in feature for debugging OpenGL ES
+applications: the <code>GLSurfaceView.setDebugFlags()</code> method can be used
+to enable logging and/or error checking your OpenGL ES calls. Call this method
+in your <code>GLSurfaceView</code>'s constructor, before calling
+<code>setRenderer()</code>:</p>
+
+<pre>public ClearGLSurfaceView(Context context) {
+ super(context);
+ // Turn on error-checking and logging
+ setDebugFlags(DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS);
+ mRenderer = new ClearRenderer();
+ setRenderer(mRenderer);
+}</pre>
\ No newline at end of file
diff --git a/docs/html/resources/articles/images/File.png b/docs/html/resources/articles/images/File.png
new file mode 100644
index 0000000..bc5a2b8
--- /dev/null
+++ b/docs/html/resources/articles/images/File.png
Binary files differ
diff --git a/docs/html/resources/articles/images/File_002.png b/docs/html/resources/articles/images/File_002.png
new file mode 100644
index 0000000..39254b30
--- /dev/null
+++ b/docs/html/resources/articles/images/File_002.png
Binary files differ
diff --git a/docs/html/resources/articles/images/JFlubber.png b/docs/html/resources/articles/images/JFlubber.png
new file mode 100644
index 0000000..d95e32b
--- /dev/null
+++ b/docs/html/resources/articles/images/JFlubber.png
Binary files differ
diff --git a/docs/html/resources/articles/images/WikiNotes.png b/docs/html/resources/articles/images/WikiNotes.png
new file mode 100644
index 0000000..d52c4fc
--- /dev/null
+++ b/docs/html/resources/articles/images/WikiNotes.png
Binary files differ
diff --git a/docs/html/resources/articles/images/all_drawables_changed.png b/docs/html/resources/articles/images/all_drawables_changed.png
new file mode 100644
index 0000000..04ec4a2
--- /dev/null
+++ b/docs/html/resources/articles/images/all_drawables_changed.png
Binary files differ
diff --git a/docs/html/resources/articles/images/android.png b/docs/html/resources/articles/images/android.png
new file mode 100644
index 0000000..6dc88cc
--- /dev/null
+++ b/docs/html/resources/articles/images/android.png
Binary files differ
diff --git a/docs/html/resources/articles/images/buttons.png b/docs/html/resources/articles/images/buttons.png
new file mode 100644
index 0000000..8c220b9
--- /dev/null
+++ b/docs/html/resources/articles/images/buttons.png
Binary files differ
diff --git a/docs/html/resources/articles/images/contacts.png b/docs/html/resources/articles/images/contacts.png
new file mode 100644
index 0000000..d8b067d
--- /dev/null
+++ b/docs/html/resources/articles/images/contacts.png
Binary files differ
diff --git a/docs/html/resources/articles/images/correct_drawables.png b/docs/html/resources/articles/images/correct_drawables.png
new file mode 100644
index 0000000..516309b
--- /dev/null
+++ b/docs/html/resources/articles/images/correct_drawables.png
Binary files differ
diff --git a/docs/html/resources/articles/images/ddms_allocation_tracker.png b/docs/html/resources/articles/images/ddms_allocation_tracker.png
new file mode 100644
index 0000000..b9fa0a1
--- /dev/null
+++ b/docs/html/resources/articles/images/ddms_allocation_tracker.png
Binary files differ
diff --git a/docs/html/resources/articles/images/ddms_allocation_trackerl.png b/docs/html/resources/articles/images/ddms_allocation_trackerl.png
new file mode 100644
index 0000000..5ac8d2a
--- /dev/null
+++ b/docs/html/resources/articles/images/ddms_allocation_trackerl.png
Binary files differ
diff --git a/docs/html/resources/articles/images/device.png b/docs/html/resources/articles/images/device.png
new file mode 100644
index 0000000..186b960
--- /dev/null
+++ b/docs/html/resources/articles/images/device.png
Binary files differ
diff --git a/docs/html/resources/articles/images/device_002.png b/docs/html/resources/articles/images/device_002.png
new file mode 100644
index 0000000..4bc3b0c
--- /dev/null
+++ b/docs/html/resources/articles/images/device_002.png
Binary files differ
diff --git a/docs/html/resources/articles/images/gestures.png b/docs/html/resources/articles/images/gestures.png
new file mode 100644
index 0000000..fe8f7cd
--- /dev/null
+++ b/docs/html/resources/articles/images/gestures.png
Binary files differ
diff --git a/docs/html/resources/articles/images/gestures_002.png b/docs/html/resources/articles/images/gestures_002.png
new file mode 100644
index 0000000..b20da98
--- /dev/null
+++ b/docs/html/resources/articles/images/gestures_002.png
Binary files differ
diff --git a/docs/html/resources/articles/images/gestures_003.png b/docs/html/resources/articles/images/gestures_003.png
new file mode 100644
index 0000000..a295939
--- /dev/null
+++ b/docs/html/resources/articles/images/gestures_003.png
Binary files differ
diff --git a/docs/html/resources/articles/images/gestures_004.png b/docs/html/resources/articles/images/gestures_004.png
new file mode 100644
index 0000000..3fe5fb1
--- /dev/null
+++ b/docs/html/resources/articles/images/gestures_004.png
Binary files differ
diff --git a/docs/html/resources/articles/images/gestures_005.png b/docs/html/resources/articles/images/gestures_005.png
new file mode 100644
index 0000000..3efc519
--- /dev/null
+++ b/docs/html/resources/articles/images/gestures_005.png
Binary files differ
diff --git a/docs/html/resources/articles/images/gestures_006.png b/docs/html/resources/articles/images/gestures_006.png
new file mode 100644
index 0000000..399c31d
--- /dev/null
+++ b/docs/html/resources/articles/images/gestures_006.png
Binary files differ
diff --git a/docs/html/resources/articles/images/grid.png b/docs/html/resources/articles/images/grid.png
new file mode 100644
index 0000000..4713de5
--- /dev/null
+++ b/docs/html/resources/articles/images/grid.png
Binary files differ
diff --git a/docs/html/resources/articles/images/ime.png b/docs/html/resources/articles/images/ime.png
new file mode 100644
index 0000000..57f6df1
--- /dev/null
+++ b/docs/html/resources/articles/images/ime.png
Binary files differ
diff --git a/docs/html/resources/articles/images/ime_002.png b/docs/html/resources/articles/images/ime_002.png
new file mode 100644
index 0000000..3ec00b2
--- /dev/null
+++ b/docs/html/resources/articles/images/ime_002.png
Binary files differ
diff --git a/docs/html/resources/articles/images/ime_003.png b/docs/html/resources/articles/images/ime_003.png
new file mode 100644
index 0000000..a3f57bb
--- /dev/null
+++ b/docs/html/resources/articles/images/ime_003.png
Binary files differ
diff --git a/docs/html/resources/articles/images/ime_004.png b/docs/html/resources/articles/images/ime_004.png
new file mode 100644
index 0000000..efeddf0
--- /dev/null
+++ b/docs/html/resources/articles/images/ime_004.png
Binary files differ
diff --git a/docs/html/resources/articles/images/ime_005.png b/docs/html/resources/articles/images/ime_005.png
new file mode 100644
index 0000000..a7394e0
--- /dev/null
+++ b/docs/html/resources/articles/images/ime_005.png
Binary files differ
diff --git a/docs/html/resources/articles/images/ime_006.png b/docs/html/resources/articles/images/ime_006.png
new file mode 100644
index 0000000..0b55c79
--- /dev/null
+++ b/docs/html/resources/articles/images/ime_006.png
Binary files differ
diff --git a/docs/html/resources/articles/images/layouts_comparison_small.png b/docs/html/resources/articles/images/layouts_comparison_small.png
new file mode 100644
index 0000000..0ba4cb8
--- /dev/null
+++ b/docs/html/resources/articles/images/layouts_comparison_small.png
Binary files differ
diff --git a/docs/html/resources/articles/images/list01.png b/docs/html/resources/articles/images/list01.png
new file mode 100644
index 0000000..e1b7fa8
--- /dev/null
+++ b/docs/html/resources/articles/images/list01.png
Binary files differ
diff --git a/docs/html/resources/articles/images/list02.png b/docs/html/resources/articles/images/list02.png
new file mode 100644
index 0000000..7f72a3f
--- /dev/null
+++ b/docs/html/resources/articles/images/list02.png
Binary files differ
diff --git a/docs/html/resources/articles/images/list_fade_1.png b/docs/html/resources/articles/images/list_fade_1.png
new file mode 100644
index 0000000..43013d6
--- /dev/null
+++ b/docs/html/resources/articles/images/list_fade_1.png
Binary files differ
diff --git a/docs/html/resources/articles/images/list_fade_2.png b/docs/html/resources/articles/images/list_fade_2.png
new file mode 100644
index 0000000..160d3ff
--- /dev/null
+++ b/docs/html/resources/articles/images/list_fade_2.png
Binary files differ
diff --git a/docs/html/resources/articles/images/list_fade_3.png b/docs/html/resources/articles/images/list_fade_3.png
new file mode 100644
index 0000000..70dca64
--- /dev/null
+++ b/docs/html/resources/articles/images/list_fade_3.png
Binary files differ
diff --git a/docs/html/resources/articles/images/list_fade_4.png b/docs/html/resources/articles/images/list_fade_4.png
new file mode 100644
index 0000000..7619fca
--- /dev/null
+++ b/docs/html/resources/articles/images/list_fade_4.png
Binary files differ
diff --git a/docs/html/resources/articles/images/merge1.jpg b/docs/html/resources/articles/images/merge1.jpg
new file mode 100644
index 0000000..114eed6
--- /dev/null
+++ b/docs/html/resources/articles/images/merge1.jpg
Binary files differ
diff --git a/docs/html/resources/articles/images/merge2.png b/docs/html/resources/articles/images/merge2.png
new file mode 100644
index 0000000..b4a8d4c
--- /dev/null
+++ b/docs/html/resources/articles/images/merge2.png
Binary files differ
diff --git a/docs/html/resources/articles/images/merge3.png b/docs/html/resources/articles/images/merge3.png
new file mode 100644
index 0000000..61ed983
--- /dev/null
+++ b/docs/html/resources/articles/images/merge3.png
Binary files differ
diff --git a/docs/html/resources/articles/images/merge4.jpg b/docs/html/resources/articles/images/merge4.jpg
new file mode 100644
index 0000000..17b6c20
--- /dev/null
+++ b/docs/html/resources/articles/images/merge4.jpg
Binary files differ
diff --git a/docs/html/resources/articles/images/merge5.png b/docs/html/resources/articles/images/merge5.png
new file mode 100644
index 0000000..289f47e
--- /dev/null
+++ b/docs/html/resources/articles/images/merge5.png
Binary files differ
diff --git a/docs/html/resources/articles/images/mutated_states.png b/docs/html/resources/articles/images/mutated_states.png
new file mode 100644
index 0000000..50518b6
--- /dev/null
+++ b/docs/html/resources/articles/images/mutated_states.png
Binary files differ
diff --git a/docs/html/resources/articles/images/on-screen-inputs.png b/docs/html/resources/articles/images/on-screen-inputs.png
new file mode 100644
index 0000000..b8b3cf7
--- /dev/null
+++ b/docs/html/resources/articles/images/on-screen-inputs.png
Binary files differ
diff --git a/docs/html/resources/articles/images/on-screen-inputs_002.png b/docs/html/resources/articles/images/on-screen-inputs_002.png
new file mode 100644
index 0000000..a5d21c7
--- /dev/null
+++ b/docs/html/resources/articles/images/on-screen-inputs_002.png
Binary files differ
diff --git a/docs/html/resources/articles/images/on-screen-inputs_003.png b/docs/html/resources/articles/images/on-screen-inputs_003.png
new file mode 100644
index 0000000..81ee257
--- /dev/null
+++ b/docs/html/resources/articles/images/on-screen-inputs_003.png
Binary files differ
diff --git a/docs/html/resources/articles/images/on-screen-inputs_004.png b/docs/html/resources/articles/images/on-screen-inputs_004.png
new file mode 100644
index 0000000..651b72a
--- /dev/null
+++ b/docs/html/resources/articles/images/on-screen-inputs_004.png
Binary files differ
diff --git a/docs/html/resources/articles/images/on-screen-inputs_005.png b/docs/html/resources/articles/images/on-screen-inputs_005.png
new file mode 100644
index 0000000..75185ff
--- /dev/null
+++ b/docs/html/resources/articles/images/on-screen-inputs_005.png
Binary files differ
diff --git a/docs/html/resources/articles/images/on-screen-inputs_006.png b/docs/html/resources/articles/images/on-screen-inputs_006.png
new file mode 100644
index 0000000..b653d75
--- /dev/null
+++ b/docs/html/resources/articles/images/on-screen-inputs_006.png
Binary files differ
diff --git a/docs/html/resources/articles/images/photostream_landscape.png b/docs/html/resources/articles/images/photostream_landscape.png
new file mode 100644
index 0000000..ad4a0c5
--- /dev/null
+++ b/docs/html/resources/articles/images/photostream_landscape.png
Binary files differ
diff --git a/docs/html/resources/articles/images/photostream_portrait.png b/docs/html/resources/articles/images/photostream_portrait.png
new file mode 100644
index 0000000..3794f63
--- /dev/null
+++ b/docs/html/resources/articles/images/photostream_portrait.png
Binary files differ
diff --git a/docs/html/resources/articles/images/qsb.png b/docs/html/resources/articles/images/qsb.png
new file mode 100644
index 0000000..4e40af1
--- /dev/null
+++ b/docs/html/resources/articles/images/qsb.png
Binary files differ
diff --git a/docs/html/resources/articles/images/qsb_002.png b/docs/html/resources/articles/images/qsb_002.png
new file mode 100644
index 0000000..8c2f772
--- /dev/null
+++ b/docs/html/resources/articles/images/qsb_002.png
Binary files differ
diff --git a/docs/html/resources/articles/images/qsb_003.png b/docs/html/resources/articles/images/qsb_003.png
new file mode 100644
index 0000000..069b6cd
--- /dev/null
+++ b/docs/html/resources/articles/images/qsb_003.png
Binary files differ
diff --git a/docs/html/resources/articles/images/relativelayout_1.png b/docs/html/resources/articles/images/relativelayout_1.png
new file mode 100644
index 0000000..3360ad8
--- /dev/null
+++ b/docs/html/resources/articles/images/relativelayout_1.png
Binary files differ
diff --git a/docs/html/resources/articles/images/relativelayout_2.png b/docs/html/resources/articles/images/relativelayout_2.png
new file mode 100644
index 0000000..8e71bb2
--- /dev/null
+++ b/docs/html/resources/articles/images/relativelayout_2.png
Binary files differ
diff --git a/docs/html/resources/articles/images/relativelayout_3.png b/docs/html/resources/articles/images/relativelayout_3.png
new file mode 100644
index 0000000..16a9767
--- /dev/null
+++ b/docs/html/resources/articles/images/relativelayout_3.png
Binary files differ
diff --git a/docs/html/resources/articles/images/relativelayout_wire_1.png b/docs/html/resources/articles/images/relativelayout_wire_1.png
new file mode 100644
index 0000000..9cb241d
--- /dev/null
+++ b/docs/html/resources/articles/images/relativelayout_wire_1.png
Binary files differ
diff --git a/docs/html/resources/articles/images/relativelayout_wire_2.png b/docs/html/resources/articles/images/relativelayout_wire_2.png
new file mode 100644
index 0000000..4243812
--- /dev/null
+++ b/docs/html/resources/articles/images/relativelayout_wire_2.png
Binary files differ
diff --git a/docs/html/resources/articles/images/relativelayout_wire_3.png b/docs/html/resources/articles/images/relativelayout_wire_3.png
new file mode 100644
index 0000000..04ce1ce
--- /dev/null
+++ b/docs/html/resources/articles/images/relativelayout_wire_3.png
Binary files differ
diff --git a/docs/html/resources/articles/images/search01.png b/docs/html/resources/articles/images/search01.png
new file mode 100644
index 0000000..4160a76
--- /dev/null
+++ b/docs/html/resources/articles/images/search01.png
Binary files differ
diff --git a/docs/html/resources/articles/images/search02.png b/docs/html/resources/articles/images/search02.png
new file mode 100644
index 0000000..6300018
--- /dev/null
+++ b/docs/html/resources/articles/images/search02.png
Binary files differ
diff --git a/docs/html/resources/articles/images/shared_states.png b/docs/html/resources/articles/images/shared_states.png
new file mode 100644
index 0000000..81bec09
--- /dev/null
+++ b/docs/html/resources/articles/images/shared_states.png
Binary files differ
diff --git a/docs/html/resources/articles/images/shelves2.png b/docs/html/resources/articles/images/shelves2.png
new file mode 100644
index 0000000..2de239f
--- /dev/null
+++ b/docs/html/resources/articles/images/shelves2.png
Binary files differ
diff --git a/docs/html/resources/articles/images/text_field.png b/docs/html/resources/articles/images/text_field.png
new file mode 100644
index 0000000..b9dedec
--- /dev/null
+++ b/docs/html/resources/articles/images/text_field.png
Binary files differ
diff --git a/docs/html/resources/articles/images/ui-1.6.png b/docs/html/resources/articles/images/ui-1.6.png
new file mode 100644
index 0000000..bc5a2b8
--- /dev/null
+++ b/docs/html/resources/articles/images/ui-1.6.png
Binary files differ
diff --git a/docs/html/resources/articles/images/ui-1.6_002.png b/docs/html/resources/articles/images/ui-1.6_002.png
new file mode 100644
index 0000000..39254b30
--- /dev/null
+++ b/docs/html/resources/articles/images/ui-1.6_002.png
Binary files differ
diff --git a/docs/html/resources/articles/images/viewstub1.png b/docs/html/resources/articles/images/viewstub1.png
new file mode 100644
index 0000000..2de239f
--- /dev/null
+++ b/docs/html/resources/articles/images/viewstub1.png
Binary files differ
diff --git a/docs/html/resources/articles/images/viewstub2.png b/docs/html/resources/articles/images/viewstub2.png
new file mode 100644
index 0000000..6e6feb9
--- /dev/null
+++ b/docs/html/resources/articles/images/viewstub2.png
Binary files differ
diff --git a/docs/html/resources/articles/images/viewstub3.png b/docs/html/resources/articles/images/viewstub3.png
new file mode 100644
index 0000000..5e793e6
--- /dev/null
+++ b/docs/html/resources/articles/images/viewstub3.png
Binary files differ
diff --git a/docs/html/resources/articles/images/viewstub4.png b/docs/html/resources/articles/images/viewstub4.png
new file mode 100644
index 0000000..cffb9c6
--- /dev/null
+++ b/docs/html/resources/articles/images/viewstub4.png
Binary files differ
diff --git a/docs/html/resources/articles/images/webview.png b/docs/html/resources/articles/images/webview.png
new file mode 100644
index 0000000..92472af
--- /dev/null
+++ b/docs/html/resources/articles/images/webview.png
Binary files differ
diff --git a/docs/html/resources/articles/images/window_background.png b/docs/html/resources/articles/images/window_background.png
new file mode 100644
index 0000000..58f4f7e
--- /dev/null
+++ b/docs/html/resources/articles/images/window_background.png
Binary files differ
diff --git a/docs/html/resources/articles/images/window_background_null.png b/docs/html/resources/articles/images/window_background_null.png
new file mode 100644
index 0000000..83f7b45d
--- /dev/null
+++ b/docs/html/resources/articles/images/window_background_null.png
Binary files differ
diff --git a/docs/html/resources/articles/images/window_background_root.png b/docs/html/resources/articles/images/window_background_root.png
new file mode 100644
index 0000000..95a47c0
--- /dev/null
+++ b/docs/html/resources/articles/images/window_background_root.png
Binary files differ
diff --git a/docs/html/resources/articles/index.jd b/docs/html/resources/articles/index.jd
new file mode 100644
index 0000000..4fda6d7
--- /dev/null
+++ b/docs/html/resources/articles/index.jd
@@ -0,0 +1,147 @@
+page.title=Technical Articles
+@jd:body
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/avoiding-memory-leaks.html">Avoiding Memory Leaks</a></dt>
+ <dd>Mobile devices often have limited memory, and memory leaks can cause your application to waste this valuable resource without your knowledge. This article provides tips to help you avoid common causes of memory leaks on the Android platform.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/backward-compatibility.html">Backward Compatibility</a></dt>
+ <dd>The Android platform strives to ensure backwards compatibility. However, sometimes you want to use new features which aren't supported on older platforms. This article discusses strategies for selectively using these features based on availability, allowing you to keep your applications portable across a wide range of devices.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/can-i-use-this-intent.html">Can I Use this Intent?</a></dt>
+ <dd>Android offers a very powerful and yet easy-to-use message type called an intent. You can use intents to turn applications into high-level libraries and make code modular and reusable. While it is nice to be able to make use of a loosely coupled API, there is no guarantee that the intent you send will be received by another application. This article describes a technique you can use to find out whether the system contains any application capable of responding to the intent you want to use.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/creating-input-method.html">Creating an Input Method</a></dt>
+ <dd>Input Method Editors (IMEs) provide the mechanism for entering text into text fields and other Views. Android devices come bundled with at least one IME, but users can install additional IMEs. This article covers the basics of developing an IME for the Android platform.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/drawable-mutations.html">Drawable Mutations</a></dt>
+ <dd>Drawables are pluggable drawing containers that allow applications to display graphics. This article explains some common pitfalls when trying to modify the properties of multiple Drawables.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/faster-screen-orientation-change.html">Faster Screen Orientation Change</a></dt>
+ <dd>When an Android device changes its orientation, the default behavior is to automatically restart the current activity with a new configuration. However, this can become a bottleneck in applications that access a large amount of external data. This article discusses how to gracefully handle this situation without resorting to manually processing configuration changes.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/future-proofing.html">Future-Proofing Your Apps</a></dt>
+ <dd>A collection of common sense advice to help you ensure that your applications don't break when new versions of the Android platform are released.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/gestures.html">Gestures</a></dt>
+ <dd>Touch screens allow users to perform gestures, such as tapping, dragging, flinging, or sliding, to perform various actions. The gestures API enables your application to recognize even complicated gestures with ease. This article explains how to integrate this API into an application.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/glsurfaceview.html">Introducing GLSurfaceView</a></dt>
+ <dd>This article provides an overview of GLSurfaceView, a class that makes it easy to implement 2D or 3D OpenGL rendering inside of an Android application.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/layout-tricks-reuse.html">Layout Tricks: Creating Reusable UI Components</a></dt>
+ <dd>Learn how to combine multiple standard UI widgets into a single high-level component, which can be reused throughout your application.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/layout-tricks-efficiency.html">Layout Tricks: Creating Efficient Layouts</a></dt>
+ <dd>Learn how to optimize application layouts as this article walks you through converting a LinearLayout into a RelativeLayout, and analyzes the resulting implications on performance.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/layout-tricks-stubs.html">Layout Tricks: Using ViewStubs</a></dt>
+ <dd>Learn about using ViewStubs inside an application's layout in order to inflate rarely used UI elements, without the performance implications which would otherwise be caused by using the <code><include></code> tag.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/layout-tricks-merge.html">Layout Tricks: Merging Layouts</a></dt>
+ <dd>Learn how to use the <code><merge></code> tag in your XML layouts in order to avoid unnecessary levels of hierarchy within an application's view tree.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/listview-backgrounds.html">ListView Backgrounds: An Optimization</a></dt>
+ <dd>ListViews are very popular widgets within the Android framework. This article describes some of the optimizations used by the ListView widget, and how to avoid some common issues that this causes when trying to use a custom background.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/live-folders.html">Live Folders</a></dt>
+ <dd>Live Folders allow users to display any source of data on their home screen without launching an application. This article discusses how to export an application's data in a format suitable for display inside of a live folder.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/on-screen-inputs.html">Onscreen Input Methods</a></dt>
+ <dd>The Input Method Framework (IMF) allows users to take advantage of on-screen input methods, such as software keyboards. This article provides an overview of Input Method Editors (IMEs) and how applications interact with them.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/painless-threading.html">Painless Threading</a></dt>
+ <dd>This article discusses the threading model used by Android applications and how applications can ensure best UI performance by spawning worker threads to handle long-running operations, rather than handling them in the main thread. The article also explains the API that your application can use to interact with Android UI toolkit components running on the main thread and spawn managed worker threads.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/qsb.html">Quick Search Box</a></dt>
+ <dd>Quick Search Box (QSB) is a powerful, system-wide search framework. QSB makes it possible for users to quickly and easily find what they're looking for, both on their devices and on the web. This article discusses how to work with the QSB framework to add new search results for an installed application.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/touch-mode.html">Touch Mode</a></dt>
+ <dd>This article explains the touch mode, one of the most important principles of Android's UI toolkit. Whenever a user interacts with a device's touch screen, the system enters touch mode. While simple in concept, there are important implications touch mode that are often overlooked.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/track-mem.html">Tracking Memory Allocations</a></dt>
+ <dd>This article discusses how to use the Allocation Tracker tool to observe memory allocations and avoid performance problems that would otherwise be caused by ignoring the effect of Dalvik's garbage collector.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/ui-1.5.html">UI Framework Changes in Android 1.5</a></dt>
+ <dd>Explore the UI changes that were introduced in Android 1.5, compared with the UI provided in Android 1.0 and 1.1.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/ui-1.6.html">UI Framework Changes in Android 1.6</a></dt>
+ <dd>Explore the UI changes that were introduced in Android 1.6, compared with the UI provided in Android 1.5. In particular, this article discusses changes to RelativeLayouts and click listeners.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/timed-ui-updates.html">Updating the UI from a Timer</a></dt>
+ <dd>Learn about how to use Handlers as a more efficient replacement for java.util.Timer on the Android platform.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/tts.html">Using Text-to-Speech</a></dt>
+ <dd>The text-to-speech API lets your application "speak" to users, in any of several languages. This article provides an overview of the TTS API and how you use to add speech capabilities to your application.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/using-webviews.html">Using WebViews</a></dt>
+ <dd>WebViews allow an application to dynamically display HTML and execute JavaScript, without relinquishing control to a separate browser application. This article introduces the WebView classes and provides a sample application that demonstrates its use.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/wikinotes-linkify.html">WikiNotes: Linkify your Text!</a></dt>
+ <dd>This article introduces WikiNotes for Android, part of the Apps for Android project. It covers the use of Linkify to turn ordinary text views into richer, link-oriented content that causes Android intents to fire when a link is selected.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/wikinotes-intents.html">WikiNotes: Routing Intents</a></dt>
+ <dd>This article illustrates how an application, in this case the WikiNotes sample app, can use intents to route various types of linked text to the application that handles that type of data. For example, an app can use intents to route a linked telephone number to a dialer app and a web URL to a browser.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/window-bg-speed.html">Window Backgrounds & UI Speed</a></dt>
+ <dd>Some Android applications need to squeeze every bit of performance out of the UI toolkit and there are many ways to do so. In this article, you will discover how to speed up the drawing and the perceived startup time of your activities. Both of these techniques rely on a single feature, the window's background drawable.</dd>
+</dl>
+
+<dl>
+ <dt><a href="{@docRoot}resources/articles/zipalign.html">Zipalign: an Easy Optimization</a></dt>
+ <dd>The Android SDK includes a tool called zipalign that optimizes the way an application is packaged. Running zipalign against your application enables Android to interact with it more efficiently at run time and thus has the potential to make it and the overall system run faster. This article provides a high-level overview of the zipalign tool and its use.</dd>
+</dl>
diff --git a/docs/html/resources/articles/layout-tricks-efficiency.jd b/docs/html/resources/articles/layout-tricks-efficiency.jd
new file mode 100644
index 0000000..b7b5761
--- /dev/null
+++ b/docs/html/resources/articles/layout-tricks-efficiency.jd
@@ -0,0 +1,177 @@
+page.title=Layout Tricks: Creating Efficient Layouts
+@jd:body
+
+<p>The Android UI toolkit offers several layout managers that are
+rather easy to use and, most of the time, you only need the basic
+features of these layout managers to implement a user interface.</p>
+
+<p>Sticking to the basic features is unfortunately not the most efficient
+way to create user interfaces. A common example is the abuse of
+{@link android.widget.LinearLayout}, which leads to a proliferation of
+views in the view hierarchy. Every view — or worse, every layout
+manager — that you add to your application comes at a cost:
+initialization, layout and drawing become slower. The layout pass can be
+especially expensive when you nest several <code>LinearLayout</code>
+that use the {@link android.R.attr#layout_weight weight}
+parameter, which requires the child to be measured twice.</p>
+
+<p>Let's consider a very simple and common example of a layout: a list item
+with an icon on the left, a title at the top and an optional description
+underneath the title. Here is what such an item looks like:</p>
+
+<div style="text-align: center;"><img src="images/relativelayout_1.png" alt="Simple list item"></div>
+
+<p>To clearly understand how the views, one {@link android.widget.ImageView} and
+two {@link android.widget.TextView}, are positioned with respect to each other,
+here is the wireframe of the layout as captured by <a
+href="{@docRoot}guide/developing/tools/hierarchy-viewer.html">HierarchyViewer</a
+>:</p>
+
+<div style="text-align: center;"><img src="images/relativelayout_wire_1.png" alt="Wireframe of the simple list item"></div>
+
+<p>Implementing this layout is straightforward with <code>LinearLayout</code>.
+The item itself is a horizontal <code>LinearLayout</code> with an
+<code>ImageView</code> and a vertical <code>LinearLayout</code>, which contains
+the two <code>TextView</code>. Here's the source code of this layout:</p>
+
+<pre class="prettyprint"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+
+ android:padding="6dip">
+
+ <ImageView
+ android:id="@+id/icon"
+
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_marginRight="6dip"
+
+ android:src="@drawable/icon" />
+
+ <LinearLayout
+ android:orientation="vertical"
+
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="fill_parent">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+
+ android:gravity="center_vertical"
+ android:text="My Application" />
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:text="Simple application that shows how to use RelativeLayout" />
+
+ </LinearLayout>
+
+</LinearLayout></pre>
+
+<p>This layout works but can be wasteful if you instantiate it for every list
+item of a {@link android.widget.ListView}. The same layout can be rewritten
+using a single {@link android.widget.RelativeLayout}, thus saving one view, and
+even better one level in view hierarchy, per list item. The implementation of
+the layout with a <code>RelativeLayout</code> remains simple:</p>
+
+<pre class="prettyprint"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+
+ android:padding="6dip">
+
+ <ImageView
+ android:id="@+id/icon"
+
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true"
+ android:layout_marginRight="6dip"
+
+ android:src="@drawable/icon" />
+
+ <TextView
+ android:id="@+id/secondLine"
+
+ android:layout_width="fill_parent"
+ android:layout_height="26dip"
+
+ android:layout_toRightOf="@id/icon"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:text="Simple application that shows how to use RelativeLayout" />
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+
+ android:layout_toRightOf="@id/icon"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_above="@id/secondLine"
+ android:layout_alignWithParentIfMissing="true"
+
+ android:gravity="center_vertical"
+ android:text="My Application" />
+
+</RelativeLayout></pre>
+
+<p>This new implementation behaves exactly the same way as the previous
+implementation, except in one case. The list item we want to display has two
+lines of text: the title and an <em>optional</em> description. When a
+description is not available for a given list item, the application would simply
+set the visibility of the second <code>TextView</code> to
+{@link android.view.View#GONE}. This works perfectly with the <code>LinearLayout</code>
+implementation but not with the <code>RelativeLayout</code> version:</p>
+
+<div style="text-align: center;"><img src="images/relativelayout_2.png" alt="RelativeLayout and description GONE"></div>
+<div style="text-align: center;"><img src="images/relativelayout_wire_2.png" alt="RelativeLayout and description GONE"></div>
+
+<p>In a <code>RelativeLayout</code>, views are aligned with their parent, with the
+<code>RelativeLayout</code> itself, or with other views. For instance, we declared that
+the description is aligned with the bottom of the <code>RelativeLayout</code> and
+that the title is positioned above the description and anchored to the
+parent's top. With the description GONE, RelativeLayout doesn't know
+where to position the title's bottom edge. To solve this problem, you
+can use a very special layout parameter called
+{@link android.R.attr#layout_alignWithParentIfMissing}.
+</p>
+
+<p>This boolean parameter simply tells RelativeLayout to use its own edges as
+anchors when a constraint target is missing. For instance, if you position a
+view to the right of a GONE view and set <code>alignWithParentIfMissing</code>
+to <code>true</code>, <code>RelativeLayout</code> will instead anchor the view
+to its left edge. In our case, using <code>alignWithParentIfMissing</code> will
+cause <code>RelativeLayout</code> to align the title's bottom with its own
+bottom. The result is the following:</p>
+
+<div style="text-align: center;"><img src="images/relativelayout_3.png" alt="RelativeLayout, description GONE and alignWithParentIfMissing"></div>
+<div style="text-align: center;"><img src="images/relativelayout_wire_3.png" alt="RelativeLayout, description GONE and alignWithParentIfMissing"></div>
+
+<p>The
+behavior of our layout is now perfect, even when the description is
+GONE. Even better, the hierarchy is simpler and because we are not
+using LinearLayout's weights it's also more efficient. The difference
+between the two implementations becomes obvious when comparing the view
+hierarchies in HierarchyViewer:</p>
+
+<div style="text-align: center;"><img src="images/layouts_comparison_small.png" alt="LinearLayout vs RelativeLayout"></div>
+
+<p>Again, the difference will be much more important when you use such a layout
+for every item in a ListView for instance. Hopefully this simple
+example showed you that getting to know your layouts is the best way to
+learn how to optimize your UI.</p>
diff --git a/docs/html/resources/articles/layout-tricks-merge.jd b/docs/html/resources/articles/layout-tricks-merge.jd
new file mode 100644
index 0000000..3e165d2
--- /dev/null
+++ b/docs/html/resources/articles/layout-tricks-merge.jd
@@ -0,0 +1,198 @@
+page.title=Layout Tricks: Merging Layouts
+@jd:body
+
+<p>The <a href=""></a> articles showed you how to use the <code><include /></code> tag in XML layouts, to reuse and share your layout code. This article explains the <code><merge /></code> tag and how it complements the <code><include /></code> tag.</p>
+
+<p>The <code><merge /></code> tag was created for the purpose of
+optimizing Android layouts by reducing the number of levels in view trees. It's
+easier to understand the problem this tag solves by looking at an example. The
+following XML layout declares a layout that shows an image with its title on top
+of it. The structure is fairly simple; a {@link android.widget.FrameLayout} is
+used to stack a {@link android.widget.TextView} on top of an
+{@link android.widget.ImageView}:</p>
+
+<pre class="prettyprint"><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+
+ android:scaleType="center"
+ android:src="@drawable/golden_gate" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="20dip"
+ android:layout_gravity="center_horizontal|bottom"
+
+ android:padding="12dip"
+
+ android:background="#AA000000"
+ android:textColor="#ffffffff"
+
+ android:text="Golden Gate" />
+
+</FrameLayout></pre>
+
+<p>This layout renders nicely and nothing seems wrong with it:</p>
+
+<div style="text-align: center;"><img src="images/merge1.jpg" alt="A FrameLayout is used to overlay a title on top of an image"></div>
+
+<p>Things get more interesting when you inspect the result with <a
+href="{@docRoot}guide/developing/tools/hierarchy-viewer.html">HierarchyViewer</a>.
+If you look closely at the resulting tree, you will notice that the
+<code>FrameLayout</code> defined in our XML file (highlighted in blue below) is
+the sole child of another <code>FrameLayout</code>:</p>
+
+<div style="text-align: center;"><img src="images/merge2.png" alt="A layout with only one child of same dimensions can be removed"></div>
+
+<p>Since our <code>FrameLayout</code> has the same dimension as its parent, by
+the virtue of using the <code>fill_parent</code> constraints, and does not
+define any background, extra padding or a gravity, it is <em>totally
+useless</em>. We only made the UI more complex for no good reason. But how could
+we get rid of this <code>FrameLayout</code>? After all, XML documents require a
+root tag and tags in XML layouts always represent view instances.</p>
+
+<p>That's where the <code><merge /></code> tag comes in handy. When the
+{@link android.view.LayoutInflater} encounters this tag, it skips it and adds
+the <code><merge /></code> children to the <code><merge /></code>
+parent. Confused? Let's rewrite our previous XML layout by replacing the
+<code>FrameLayout</code> with <code><merge /></code>:</p>
+
+<pre class="prettyprint"><merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+
+ android:scaleType="center"
+ android:src="@drawable/golden_gate" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="20dip"
+ android:layout_gravity="center_horizontal|bottom"
+
+ android:padding="12dip"
+
+ android:background="#AA000000"
+ android:textColor="#ffffffff"
+
+ android:text="Golden Gate" />
+
+</merge></pre>
+
+<p>With this new version, both the <code>TextView</code> and the
+<code>ImageView</code> will be added directly to the top-level
+<code>FrameLayout</code>. The result will be visually the same but the view
+hierarchy is simpler:</p>
+
+<div style="text-align: center;"><img src="images/merge3.png" alt="Optimized view hierarchy using the merge tag"></div>
+
+<p>Obviously, using <code><merge /></code> works in this case because the
+parent of an activity's content view is always a <code>FrameLayout</code>. You
+could not apply this trick if your layout was using a <code>LinearLayout</code>
+as its root tag for instance. The <code><merge /></code> can be useful in
+other situations though. For instance, it works perfectly when combined with the
+<code><include /></code> tag. You can also use <code><merge
+/></code> when you create a custom composite view. Let's see how we can use
+this tag to create a new view called <code>OkCancelBar</code> which simply shows
+two buttons with customizable labels. You can also <a
+href="http://progx.org/users/Gfx/android/MergeLayout.zip">download the complete
+source code of this example</a>. Here is the XML used to display this custom
+view on top of an image:</p>
+
+<pre class="prettyprint"><merge
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:okCancelBar="http://schemas.android.com/apk/res/com.example.android.merge">
+
+ <ImageView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+
+ android:scaleType="center"
+ android:src="@drawable/golden_gate" />
+
+ <com.example.android.merge.OkCancelBar
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+
+ android:paddingTop="8dip"
+ android:gravity="center_horizontal"
+
+ android:background="#AA000000"
+
+ okCancelBar:okLabel="Save"
+ okCancelBar:cancelLabel="Don't save" />
+
+</merge></pre>
+
+<p>This new layout produces the following result on a device:</p>
+
+<div style="text-align: center;"><img src="images/merge4.jpg" alt="Creating a custom view with the merge tag"></div>
+
+<p>The source code of <code>OkCancelBar</code> is very simple because the two
+buttons are defined in an external XML file, loaded using a
+<code>LayoutInflate</code>. As you can see in the following snippet, the XML
+layout <code>R.layout.okcancelbar</code> is inflated with the
+<code>OkCancelBar</code> as the parent:</p>
+
+<pre class="prettyprint">public class OkCancelBar extends LinearLayout {
+ public OkCancelBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setOrientation(HORIZONTAL);
+ setGravity(Gravity.CENTER);
+ setWeightSum(1.0f);
+
+ LayoutInflater.from(context).inflate(R.layout.okcancelbar, this, true);
+
+ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.OkCancelBar, 0, 0);
+
+ String text = array.getString(R.styleable.OkCancelBar_okLabel);
+ if (text == null) text = "Ok";
+ ((Button) findViewById(R.id.okcancelbar_ok)).setText(text);
+
+ text = array.getString(R.styleable.OkCancelBar_cancelLabel);
+ if (text == null) text = "Cancel";
+ ((Button) findViewById(R.id.okcancelbar_cancel)).setText(text);
+
+ array.recycle();
+ }
+}</pre>
+
+<p>The two buttons are defined in the following XML layout. As you can see, we
+use the <code><merge /></code> tag to add the two buttons directly to the
+<code>OkCancelBar</code>. Each button is included from the same external XML
+layout file to make them easier to maintain; we simply override their id:</p>
+
+<pre class="prettyprint"><merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <include
+ layout="@layout/okcancelbar_button"
+ android:id="@+id/okcancelbar_ok" />
+
+ <include
+ layout="@layout/okcancelbar_button"
+ android:id="@+id/okcancelbar_cancel" />
+</merge></pre>
+
+<p>We have created a flexible and easy to maintain custom view that generates
+an efficient view hierarchy:</p>
+
+<div style="text-align: center;"><img src="images/merge5.png" alt="The resulting hierarchy is simple and efficient"></div>
+
+<p>The <code><merge /></code> tag is extremely useful and can do wonders
+in your code. However, it suffers from a couple of limitations:</p>
+
+<ul>
+<li><code><merge /></code> can only be used as the root tag of an XML layout</li>
+<li>When inflating a layout starting with a <code><merge /></code>, you <strong>must</strong>
+specify a parent <code>ViewGroup</code> and you must set <code>attachToRoot</code> to
+<code>true</code> (see the documentation for
+{@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)} method)</li>
+</ul>
+
diff --git a/docs/html/resources/articles/layout-tricks-reuse.jd b/docs/html/resources/articles/layout-tricks-reuse.jd
new file mode 100644
index 0000000..072ba89
--- /dev/null
+++ b/docs/html/resources/articles/layout-tricks-reuse.jd
@@ -0,0 +1,67 @@
+page.title=Layout Tricks: Creating Reusable UI Components
+@jd:body
+
+<p>The Android platform offers a wide variety of UI <em>widgets</em>, small
+visual construction blocks that you can glue together to present users with
+complex and useful interfaces. However applications often need higher-level
+visual <em>components</em>. To meet that need, and to do so efficiently, you can
+combine multiple standard widgets into a single, reusable component. </p>
+
+<p>For example, you could create a reusable component that contains a progress
+bar and a cancel button, a panel containing two buttons (positive and negative
+actions), a panel with an icon, a title and a description, and so on. You can
+create UI components easily by writing a custom <code>View</code>, but you can
+do it even more easily using only XML.</p>
+
+<p>In Android XML layout files, each tag is mapped to an actual class instance
+(the class is always a subclass of {@link android.view.View} The UI toolkit lets
+you also use three special tags that are not mapped to a <code>View</code>
+instance: <code><requestFocus /></code>, <code><merge /></code> and
+<code><include /></code>. This article shows how to use <code><include
+/></code> to create pure XML visual components. For information about how to
+use <code><merge /></code>, which can be particularly powerful when
+combined with <code><include /></code>see the <a
+href="{@docRoot}resources/articles/layout-tricks-merge.html">Merging Layouts</a>
+article. </p>
+
+<p>The <code><include /></code> element does exactly what its name
+suggests; it includes another XML layout. Using this tag is straightforward as
+shown in the following example, taken straight from <a
+href="http://android.git.kernel.org/?p=platform/packages/apps/Launcher.git;a=
+tree;h=refs/heads/master;hb=master">the source code of the Home application</a>
+that ships with Android:</p>
+
+<pre class="prettyprint"><com.android.launcher.Workspace
+ android:id="@+id/workspace"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+
+ launcher:defaultScreen="1">
+
+ <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
+ <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
+ <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
+
+</com.android.launcher.Workspace></pre>
+
+<p>In the <code><include /></code> only the <code>layout</code> attribute
+is required. This attribute, without the <code>android</code> namespace prefix,
+is a reference to the layout file you wish to include. In this example, the same
+layout is included three times in a row. This tag also lets you override a few
+attributes of the included layout. The above example shows that you can use
+<code>android:id</code> to specify the id of the root view of the included
+layout; it will also override the id of the included layout if one is defined.
+Similarly, you can override all the layout parameters. This means that any
+<code>android:layout_*</code> attribute can be used with the <code><include
+/></code> tag. Here is an example:</p>
+
+<pre class="prettyprint"><include android:layout_width="fill_parent" layout="@layout/image_holder" />
+<include android:layout_width="256dip" layout="@layout/image_holder" />
+</pre>
+
+<p>This tag is particularly useful when you need to customize only part of your
+UI depending on the device's configuration. For instance, the main layout of
+your activity can be placed in the <code>layout/</code> directory and can
+include another layout which exists in two flavors, in <code>layout-land/</code>
+and <code>layout-port/</code>. This allows you to share most of the UI in
+portrait and landscape.</p>
\ No newline at end of file
diff --git a/docs/html/resources/articles/layout-tricks-stubs.jd b/docs/html/resources/articles/layout-tricks-stubs.jd
new file mode 100644
index 0000000..e494a54
--- /dev/null
+++ b/docs/html/resources/articles/layout-tricks-stubs.jd
@@ -0,0 +1,84 @@
+page.title=Layout Tricks: Using ViewStubs
+@jd:body
+
+<p>Sharing and reusing UI components is very easy with Android, thanks to the <a
+href="layout-tricks-include.html"><include /></a> tag. Sometimes it's so
+easy to create complex UI constructs that your UI ends up with a large number of
+views, some of which are rarely used. Thankfully, Android offers a very special
+widget called {@link android.view.ViewStub}, which brings you all the benefits
+of the <code><include /></code> without polluting your user interface with
+rarely used views.</p>
+
+<p>A <code>ViewStub</code> is a dumb and lightweight view. It has no dimension,
+it does not draw anything and does not participate in the layout in any way.
+This means that a <code>ViewStub</code> is very cheap to inflate and very cheap
+to keep in a view hierarchy. A <code>ViewStub</code> can be best described as a
+<em>lazy include</em>. The layout referenced by a <code>ViewStub</code> is
+inflated and added to the user interface only when you decide so.</p>
+
+<p>The following screenshot comes from the <a
+href="http://code.google/p/shelves">Shelves</a> application. The main purpose of
+the activity shown in the screenshot is to present the user with a browsable
+list of books:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center;" src="images/viewstub1.png" alt="" id="BLOGGER_PHOTO_ID_5314039375336055346" border="0">
+
+<p>The same activity is also used when the user adds or imports new books.
+During such an operation, Shelves shows extra bits of user interface.
+The screenshot below shows the progress bar and cancel button that
+appear at the bottom of the screen during an import:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center;" src="images/viewstub2.png" alt="" id="BLOGGER_PHOTO_ID_5314039800002559378" border="0">
+
+<p>Because importing books is not a common operation, at least when compared to
+browsing the list of books, the import panel is originally represented
+by a <code>ViewStub</code>:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center;" src="images/viewstub3.png" alt="" id="BLOGGER_PHOTO_ID_5314040334008167378" border="0">
+
+<p>When the user initiates the import process, the <code>ViewStub</code> is
+inflated and replaced by the content of the layout file it references:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center;" src="images/viewstub4.png" alt="" id="BLOGGER_PHOTO_ID_5314040638747834450" border="0">
+
+<p>To use a <code>ViewStub</code>, all you need is to specify an
+<code>android:id</code> attribute, to later inflate the stub, and an
+<code>android:layout</code> attribute, to reference what layout file
+to include and inflate. A stub lets you use a third attribute,
+<code>android:inflatedId</code>, which can be used to override the
+<code>id</code> of the root of the included file. Finally, the layout
+parameters specified on the stub will be applied to the roof of the
+included layout. Here is an example:</p>
+
+<pre class="prettyprint"><ViewStub
+ android:id="@+id/stub_import"
+ android:inflatedId="@+id/panel_import"
+
+ android:layout="@layout/progress_overlay"
+
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom" /></pre>
+
+<p>When you are ready to inflate the stub, simply invoke the
+{@link android.view.ViewStub#inflate()} method. You can also simply change the
+visibility of the stub to {@link android.view.View#VISIBLE} or
+{@link android.view.View#INVISIBLE} and the stub will inflate. Note however that the
+<code>inflate()</code> method has the benefit of returning the root
+<code>View</code> of the inflate layout:</p>
+
+<pre class="prettyprint">((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
+// or
+View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();</pre>
+
+<p>It is very important to remember that after the stub is inflated, the stub is
+<em>removed</em> from the view hierarchy. As such, it is unnecessary to keep a
+long-lived reference, for instance in an class instance field, to a
+<code>ViewStub</code>.</p>
+
+<p>A <code>ViewStub</code> is a great compromise between ease of programming and
+efficiency. Instead of inflating views manually and adding them at runtime to
+your view hierarchy, simply use a <code>ViewStub</code>. It's cheap and easy.
+The only drawback of <code>ViewStub</code> is that it currently does
+<em>not</em> support the <a href="layout-tricks-merge.html"><merge />
+tag</a>.</p>
diff --git a/docs/html/resources/articles/listview-backgrounds.jd b/docs/html/resources/articles/listview-backgrounds.jd
new file mode 100644
index 0000000..f4c6998
--- /dev/null
+++ b/docs/html/resources/articles/listview-backgrounds.jd
@@ -0,0 +1,86 @@
+page.title=ListView Backgrounds: An Optimization
+@jd:body
+
+<p>{@link android.widget.ListView} is one of Android's most widely used widgets.
+It is rather easy to use, very flexible, and incredibly powerful.
+<code>ListView</code> can also be difficult to understand at times.</p>
+
+<p>One of the most common issues with <code>ListView</code> happens when you try
+to use a custom background. By default, like many Android widgets,
+<code>ListView</code> has a transparent background which means that you can see
+through the default window's background, a very dark gray
+(<code>#FF191919</code> with the current <code>dark</code> theme.) Additionally,
+<code>ListView</code> enables the <em>fading edges</em> by default, as you can
+see at the top of the following screenshot — the first text item gradually
+fades to black. This technique is used throughout the system to indicate that
+the container can be scrolled.</p>
+
+<div style="text-align: center;"><img src="images/list_fade_1.png" alt="Android's default ListView"></div>
+
+<p>The fade effect is implemented using a combination of
+{@link android.graphics.Canvas#saveLayerAlpha(float, float, float, float, int, int) Canvas.saveLayerAlpha()}
+and the {@link android.graphics.PorterDuff.Mode#DST_OUT Porter-Duff Destination Out blending mode}. </p>
+
+<p>Unfortunately, things start to get ugly when you try to use a custom
+background on the <code>ListView</code> or when you change the window's
+background. The following two screenshots show what happens in an application
+when you change the window's background. The left image shows what the list
+looks like by default and the right image shows what the list looks like during
+a scroll initiated with a touch gesture:</p>
+
+<div style="text-align: center;">
+<img style="margin-right: 12px;" src="images/list_fade_2.png" alt="Dark fade">
+<img src="images/list_fade_3.png" alt="Dark list"></div>
+
+<p>This rendering issue is caused by an optimization of the Android framework
+enabled by default on all instances of <code>ListView</code>. I mentioned
+earlier that the fade effect is implemented using a Porter-Duff blending mode.
+This implementation works really well but is unfortunately very costly and can
+bring down drawing performance by quite a bit as it requires to capture a
+portion of the rendering in an offscreen bitmap and then requires extra blending
+(which implies readbacks from memory.)</p>
+
+<p>Since <code>ListView</code> is most of the time displayed on a solid
+background, there is no reason to go down that expensive route. That's why we
+introduced an optimization called the "cache color hint." The cache color hint
+is an RGB color set by default to the window's background color, that is #191919
+in Android's dark theme. When this hint is set, <code>ListView</code> (actually,
+its base class <code>View</code>) knows it will draw on a solid background and
+therefore replaces th expensive <code>saveLayerAlpha()/Porter-Duff</code>
+rendering with a simple gradient. This gradient goes from fully transparent to
+the cache color hint value and this is exactly what you see on the image above,
+with the dark gradient at the bottom of the list. However, this still does not
+explain why the entire list turns black during a scroll.</p>
+
+<p>As mentioned before, <code>ListView</code> has a transparent/translucent
+background by default, and so all default widgets in the Android UI toolkit.
+This implies that when <code>ListView</code> redraws its children, it has to
+blend the children with the window's background. Once again, this requires
+costly readbacks from memory that are particularly painful during a scroll or a
+fling when drawing happens dozen of times per second. </p>
+
+<p>To improve drawing performance during scrolling operations, the Android
+framework reuses the cache color hint. When this hint is set, the framework
+copies each child of the list in a <code>Bitmap</code> filled with the hint
+value (assuming that another optimization, called <em>scrolling cache</em>, is
+not turned off). <code>ListView</code> then blits these bitmaps directly on
+screen and because these bitmaps are known to be opaque, no blending is
+required. Also, since the default cache color hint is <code>#191919</code>, you
+get a dark background behind each item during a scroll.</p>
+
+<p>To fix this issue, all you have to do is either disable the cache color hint
+optimization, if you use a non-solid color background, or set the hint to the
+appropriate solid color value. You can do this from code (see
+{@link android.widget.AbsListView#setCacheColorHint(int)}) or preferably from
+XML, by using the <code>android:cacheColorHint</code> attribute. To disable the
+optimization, simply use the transparent color <code>#00000000</code>. The
+following screenshot shows a list with
+<code>android:cacheColorHint="#00000000"</code> set in the XML layout file:</p>
+
+<div style="text-align: center;"><img src="images/list_fade_4.png" alt="Fade on a custom background"></div>
+
+<p>As you can see, the fade works perfectly against the custom wooden
+background. The cache color hint feature is interesting because it
+shows how optimizations can make your life more difficult in
+some situations. In this particular case, however, the benefit of the
+default behavior outweighs the added complexity..</p>
diff --git a/docs/html/resources/articles/live-folders.jd b/docs/html/resources/articles/live-folders.jd
new file mode 100644
index 0000000..be974f4
--- /dev/null
+++ b/docs/html/resources/articles/live-folders.jd
@@ -0,0 +1,168 @@
+page.title=Live Folders
+@jd:body
+
+<p>Live folders, introduced in Android 1.5 (API Level 3), let you display any source of data
+on the Home screen without forcing the user to launch an application. A live
+folder is simply a real-time view of a {@link android.content.ContentProvider}.
+As such, a live folder can be used to display all of the user's contacts or
+bookmarks, email, playlists, an RSS feed, and so on. The possibilities are
+endless! </p>
+
+<p>The platform includes several standard folders for displaying contacts. For
+instance, the screenshot below shows the content of the live folders that
+displays all contacts with a phone number:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 267px; height: 400px;" src="images/contacts.png" alt="" id="BLOGGER_PHOTO_ID_5323287788220889218" border="0">
+
+<p>If a contacts sync happens in the background while the user is browsing this live
+folder, the user will see the change happen in real-time. Live folders are not
+only useful, but they are also easy to add to to your application and data.
+
+This articles shows how to add a live folder to an example application called
+Shelves. To better understand how live folders work, you can <a
+href="http://code.google.com/p/shelves">download the source code of the
+application</a> and modify it by following the instructions below.</p>
+
+<p>To give the user the option to create a new live folder for an application,
+you first need to create a new activity with an intent filter whose action is
+<code>android.intent.action.CREATE_LIVE_FOLDER</code>. To do so, simply open
+<code>AndroidManifest.xml</code> and add something similar to this:</p>
+
+<pre><activity
+ android:name=".activity.BookShelfLiveFolder"
+ android:label="BookShelf">
+ <intent-filter>
+ <action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+</activity></pre>
+
+<p>The label and icon of this activity are what the user will see on the Home
+screen when choosing a live folder to create:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 267px; height: 400px;" src="images/device_002.png" alt="" id="BLOGGER_PHOTO_ID_5323289217773103922" border="0">
+
+<p>Since you just need an intent filter, it is possible, and sometimes advised,
+to reuse an existing activity. In the case of Shelves, we will create a new
+activity, <code>org.curiouscreature.android.shelves.activity.BookShelfLiveFolder</code>.
+The role of this activity is to send an <code>Intent</code> result to Home
+containing the description of the live folder: its name, icon, display mode and
+content URI. The content URI is very important as it describes what
+<code>ContentProvider</code> will be used to populate the live folder. The code
+of the activity is very simple as you can see here:</p>
+
+<pre>public class BookShelfLiveFolder extends Activity {
+ public static final Uri CONTENT_URI = Uri.parse("content://shelves/live_folders/books");
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Intent intent = getIntent();
+ final String action = intent.getAction();
+
+ if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {
+ setResult(RESULT_OK, createLiveFolder(this, CONTENT_URI,
+ "Books", R.drawable.ic_live_folder));
+ } else {
+ setResult(RESULT_CANCELED);
+ }
+
+ finish();
+ }
+
+ private static Intent createLiveFolder(Context context, Uri uri, String name, int icon) {
+ final Intent intent = new Intent();
+
+ intent.setData(uri);
+ intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, name);
+ intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON,
+ Intent.ShortcutIconResource.fromContext(context, icon));
+ intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE, LiveFolders.DISPLAY_MODE_LIST);
+
+ return intent;
+ }
+}</pre>
+
+<p>This activity, when invoked with the<code>ACTION_CREATE_LIVE_FOLDER</code>
+intent, returns an intent with a URI,
+<code>content://shelves/live_folders/books</code>, and three extras to describe
+the live folder. There are other extras and constants you can use and you should
+refer to the documentation of <code>android.provider.LiveFolders</code> for more
+details. When Home receives this intent, a new live folder is created on the
+user's desktop, with the name and icon you provided. Then, when the user clicks
+on the live folder to open it, Home queries the content provider referenced by
+the provided URI.</p>
+
+<p>Live folders' content providers must obey specific naming rules. The
+<code>Cursor</code> returned by the <code>query()</code> method must have at
+least two columns named <code>LiveFolders._ID</code> and
+<code>LiveFolders.NAME</code>. The first one is the unique identifier of each
+item in the live folder and the second one is the name of the item. There are
+other column names you can use to specify an icon, a description, the intent to
+associate with the item (fired when the user clicks that item), etc. Again,
+refer to the documentation of <code>android.provider.LiveFolders</code> for more
+details.</p><p>In our example, all we need to do is modify the existing provider
+in Shelves called
+<code>org.curiouscreature.android.shelves.provider.BooksProvider</code>. First,
+we need to modify the <code>URI_MATCHER</code> to recognize our
+<code>content://shelves/live_folders/books</code> content URI:</p>
+
+<pre>private static final int LIVE_FOLDER_BOOKS = 4;
+// ...
+URI_MATCHER.addURI(AUTHORITY, "live_folders/books", LIVE_FOLDER_BOOKS);</pre>
+
+<p>Then we need to create a new projection map for the cursor. A projection map
+can be used to "rename" columns. In our case, we will replace
+<code>BooksStore.Book._ID</code>, <code>BooksStore.Book.TITLE</code> and
+<code>BooksStore.Book.AUTHORS</code> with <code>LiveFolders._ID</code>,
+<code>LiveFolders.TITLE</code> and <code>LiveFolders.DESCRIPTION</code>:</p>
+
+<pre>private static final HashMap<string, string=""> LIVE_FOLDER_PROJECTION_MAP;
+static {
+ LIVE_FOLDER_PROJECTION_MAP = new HashMap<string, string="">();
+ LIVE_FOLDER_PROJECTION_MAP.put(LiveFolders._ID, BooksStore.Book._ID +
+ " AS " + LiveFolders._ID);
+ LIVE_FOLDER_PROJECTION_MAP.put(LiveFolders.NAME, BooksStore.Book.TITLE +
+ " AS " + LiveFolders.NAME);
+ LIVE_FOLDER_PROJECTION_MAP.put(LiveFolders.DESCRIPTION, BooksStore.Book.AUTHORS +
+ " AS " + LiveFolders.DESCRIPTION);
+}</pre>
+
+<p>Because we are providing a title and a description for each row, Home will
+automatically display each item of the live folder with two lines of text.
+Finally, we implement the <code>query()</code> method by supplying our
+projection map to the SQL query builder:</p>
+
+<pre>public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+
+ switch (URI_MATCHER.match(uri)) {
+ // ...
+ case LIVE_FOLDER_BOOKS:
+ qb.setTables("books");
+ qb.setProjectionMap(LIVE_FOLDER_PROJECTION_MAP);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, BooksStore.Book.DEFAULT_SORT_ORDER);
+ c.setNotificationUri(getContext().getContentResolver(), uri);
+
+ return c;
+}</pre>
+
+<p>You can now compile and deploy the application, go to the Home screen and
+try to add a live folder. You can add a books live folder to your Home screen
+and when you open it, see the list of all of your books, with their
+titles and authors, and all it took was a few lines of code:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 267px; height: 400px;" src="images/device.png" alt="" id="BLOGGER_PHOTO_ID_5323293545495859234" border="0"></p>
+
+<p>The live folders API is extremely simple and relies only on intents and
+content URI. If you want to see more examples of live folders
+implementation, you can read the source code of the <a href="http://android.git.kernel.org/?p=platform/packages/apps/Contacts.git;a=tree;h=refs/heads/cupcake;hb=cupcake">Contacts application</a> and of the <a href="http://android.git.kernel.org/?p=platform/packages/providers/ContactsProvider.git;a=tree;h=refs/heads/cupcake;hb=cupcake">Contacts provider</a>.</p><p>You can also download the result of our exercise, the <a href="http://jext.free.fr/CupcakeShelves.zip">modified version of Shelves with live folders support</a>.</p>
\ No newline at end of file
diff --git a/docs/html/resources/articles/on-screen-inputs.jd b/docs/html/resources/articles/on-screen-inputs.jd
new file mode 100644
index 0000000..057325a
--- /dev/null
+++ b/docs/html/resources/articles/on-screen-inputs.jd
@@ -0,0 +1,249 @@
+page.title=Onscreen Input Methods
+@jd:body
+
+
+<p>Starting from Android 1.5, the Android platform offers an Input Method
+Framework (IMF) that lets you create on-screen input methods such as software
+keyboards. This article provide an overview of what Android input method editors
+(IMEs) are and what an application needs to do to work well with them. The IMF
+is designed to support new classes of Android devices, such as those without
+hardware keyboards, so it is important that your application works well with the
+IMF and offers a great experience for users.</p>
+
+<h3>What is an input method?</h3>
+
+<p>The Android IMF is designed to support a variety of IMEs, including soft
+keyboard, hand-writing recognizers, and hard keyboard translators. Our focus,
+however, will be on soft keyboards, since this is the kind of input method that
+is currently part of the platform.</p>
+
+<p>A user will usually access the current IME by tapping on a text view to
+edit, as shown here in the home screen:</p>
+
+<img style="width: 320px; height: 480px; margin-right: 10px;" src="images/on-screen-inputs_004.png">
+<img style="width: 320px; height: 480px;" src="images/on-screen-inputs.png">
+
+<p>The soft keyboard is positioned at the bottom of the screen over the
+application's window. To organize the available space between the application
+and IME, we use a few approaches; the one shown here is called <em>pan and
+scan</em>, and simply involves scrolling the application window around so that
+the currently focused view is visible. This is the default mode, since it is the
+safest for existing applications.</p>
+
+<p>Most often the preferred screen layout is a <em>resize</em>, where the
+application's window is resized to be entirely visible. An example is shown
+here, when composing an e-mail message:</p>
+
+<img style="width: 320px; height: 480px; margin-right: 10px;" src="images/on-screen-inputs_005.png">
+<img style="width: 320px; height: 480px;" src="images/on-screen-inputs_003.png">
+
+<p>The size of the application window is changed so that none of it is hidden by
+the IME, allowing full access to both the application and IME. This of course
+only works for applications that have a resizeable area that can be reduced to
+make enough space, but the vertical space in this mode is actually no less than
+what is available in landscape orientation, so very often an application can
+already accommodate it.</p>
+
+<p>The final major mode is <em>fullscreen</em> or <em>extract</em>
+mode. This is used when the IME is too large to reasonably share space
+with the underlying application. With the standard IMEs, you will only
+encounter this situation when the screen is in a landscape orientation,
+although other IMEs are free to use it whenever they desire. In this
+case the application window is left as-is, and the IME simply displays
+fullscreen on top of it, as shown here:</p>
+
+<img style="width: 480px; height: 320px; margin-right: 10px; margin-bottom: 10px;" src="images/on-screen-inputs_006.png">
+<img style="width: 480px; height: 320px;" src="images/on-screen-inputs_002.png">
+
+<p>Because the IME is covering the application, it has its own editing area,
+which shows the text actually contained in the application. There are also some
+limited opportunities the application has to customize parts of the IME (the
+"done" button at the top and enter key label at the bottom) to improve the user
+experience.</p>
+
+<h3>Basic XML attributes for controlling IMEs</h3>
+
+<p>There are a number of things the system does to try to help existing
+applications work with IMEs as well as possible, such as:</p>
+
+<ul>
+<li>Use pan and scan mode by default, unless it can reasonably guess that
+resize mode will work by the existence of lists, scroll views, etc.</li>
+<li>Analyze the various existing TextView attributes to guess at the kind of
+content (numbers, plain text, etc) to help the soft keyboard display an
+appropriate key layout.</li>
+<li>Assign a few default actions to the fullscreen IME, such as "next field"
+and "done".</li>
+</ul>
+
+<p>There are also some simple things you can do in your application that will
+often greatly improve its user experience. Except where explicitly mentioned,
+these will work in any Android platform version, even those previous to Android
+1.5 (since they will simply ignore these new options).</p>
+
+<h4>Specifying each EditText control's input type</h4>
+
+<p>The most important thing for an application to do is to use the new
+<code>android:inputType</code>
+attribute on each <code>EditText</code>. The attribute provides much richer
+information
+about the text content. This attribute actually replaces many existing
+attributes (<code>android:</code><code>password</code>,
+<code>android:</code><code>singleLine</code>,
+<code>android:</code><code>numeric</code>,
+<code>android:</code><code>phoneNumber</code>,
+<code>android:</code><code>capitalize</code>,
+<code>android:</code><code>autoText</code>, and
+<code>android:</code><code>editable</code>). If you specify the older attributes
+and the new <code>android:inputType</code> attribute, the system uses
+<code>android:inputType</code> and ignores the others. </p>
+
+<p>The <code>android:inputType</code> attribute has three pieces:</p>
+
+<ul>
+<li>The <em>class</em> is the overall interpretation of characters. The
+currently supported classes are <code>text</code> (plain text),
+<code>number</code> (decimal number), <code>phone</code> (phone number), and
+<code>datetime</code> (a date or time).</li>
+<li>The <em>variation</em> is a further refinement on the class. In the
+attribute you will normally specify the class and variant together, with the
+class as a prefix. For example, <code>textEmailAddress</code> is a text field
+where the user will enter something that is an e-mail address (foo@bar.com) so
+the key layout will have an '@' character in easy access, and
+<code>numberSigned</code> is a numeric field with a sign. If only the class is
+specified, then you get the default/generic variant.</li>
+<li>Additional <em>flags</em> can be specified that supply further refinement.
+These flags are specific to a class. For example, some flags for the
+<code>text</code> class are <code>textCapSentences</code>,
+<code>textAutoCorrect</code>, and <code>textMultiline</code>.</li>
+</ul>
+
+<p>As an example, here is the new EditText for the IM application's message text view:</p>
+
+<pre> <EditText android:id="@+id/edtInput"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textShortMessage|textAutoCorrect|textCapSentences|textMultiLine"
+ android:imeOptions="actionSend|flagNoEnterAction"
+ android:maxLines="4"
+ android:maxLength="2000"
+ android:hint="@string/compose_hint"/></pre>
+
+<p>A full description of all of the input types can be found in the
+documentation. It is important to make use of the correct input types that are
+available, so that the soft keyboard can use the optimal keyboard layout for the
+text the user will be entering.</p>
+
+<h4>Enabling resize mode and other window features</h4>
+
+<p>The second most important thing for your app to do is to specify the overall
+behavior of your window in relation to the input method. The most visible aspect
+of this is controlling resize vs. pan and scan mode, but there are other things
+you can do as well to improve your user experience.</p>
+
+<p>You will usually control this behavior through the
+<code>android:windowSoftInputMode</code> attribute on each
+<code><activity></code> definition in your
+<code>AndroidManifest.xml</code>. Like the input type, there are a couple
+different pieces of data that can be specified here by combining them
+together:</p>
+
+<ul>
+<li>The window adjustment mode is specified with either
+<code>adjustResize</code> or <code>adjustPan</code>. It is highly recommended
+that you always specify one or the other.</li>
+<li>You can further control whether the IME will be shown automatically when
+your activity is displayed and other situations where the user moves to it. The
+system won't automatically show an IME by default, but in some cases it can be
+convenient for the user if an application enables this behavior. You can request
+this with <code>stateVisible</code>. There are also a number of other state
+options for finer-grained control that you can find in the documentation.</li>
+</ul>
+
+<p>A typical example of this field can be see in the edit contact activity,
+which ensures it is resized and automatically displays the IME for the user:</p>
+
+<pre> <activity name="EditContactActivity"
+ android:windowSoftInputMode="stateVisible|adjustResize">
+ ...
+ </activity></pre>
+
+<p class="note"><strong>Note:</strong>Starting from Android 1.5 (API Level 3),
+the platform offers a new method,
+{@link android.view.Window#setSoftInputMode(int mode)},
+that non-Activity windows can use to control their behavior. Calling this method
+in your will make your application incompatible with previous versions of the
+Android platform.</p>
+
+<h4>Controlling the action buttons</h4>
+
+<p>The final customization we will look at is the "action" buttons in the IME.
+There are currently two types of actions:</p>
+
+<ul>
+<li>The enter key on a soft keyboard is typically bound to an action when not
+operating on a mult-line edit text. For example, on the G1 pressing the hard
+enter key will typically move to the next field or the application will
+intercept it to execute an action; with a soft keyboard, this overloading of the
+enter key remains, since the enter button just sends an enter key event.</li>
+<li>When in fullscreen mode, an IME may also put an additional action button to
+the right of the text being edited, giving the user quick access to a common
+application operation.</li>
+</ul>
+
+<p>These options are controlled with the <code>android:imeOptions</code>
+attribute on <code>TextView</code>. The value you supply here can be any
+combination of:</p>
+
+<ul>
+<li>One of the pre-defined action constants (<code>actionGo</code>,
+<code>actionSearch</code>, <code>actionSend</code>, <code>actionNext</code>,
+<code>actionDone</code>). If none of these are specified, the system will infer
+either <code>actionNext</code> or <code>actionDone</code> depending on whether
+there is a focusable field after this one; you can explicitly force no action
+with <code>actionNone</code>.</li>
+<li>The <code>flagNoEnterAction</code> option tells the IME that the action
+should <em>not</em> be available on the enter key, even if the text itself is
+not multi-line. This avoids having unrecoverable actions like (send) that can be
+accidentally touched by the user while typing.</li>
+<li>The <code>flagNoAccessoryAction</code> removes the action button from the
+text area, leaving more room for text.</li><li>The <code>flagNoExtractUi</code>
+completely removes the text area, allowing the application to be seen behind
+it.</li>
+</ul>
+
+<p>The previous IM application message view also provides an example of an
+interesting use of <code>imeOptions</code>, to specify the send action but not
+let it be shown on the enter key:</p>
+
+<pre>android:imeOptions="actionSend|flagNoEnterAction"</pre>
+
+<h3>APIs for controlling IMEs</h3>
+
+<p>For more advanced control over the IME, there are a variety of new APIs you
+can use. Unless special care is taken (such as by using reflection), using these
+APIs will cause your application to be incompatible with previous versions of
+Android, and you should make sure you specify
+<code>android:minSdkVersion="3"</code> in your manifest. For more information,
+see the documentation for the <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><uses-sdk></a
+> manifest element.</p>
+
+<p>The primary API is the new <code>android.view.inputmethod.InputMethodManager</code> class, which you can retrieve with <code>Context.getSystemService()</code>.
+It allows you to interact with the global input method state, such as
+explicitly hiding or showing the current IME's input area.</p>
+
+<p>There are also new window flags controlling input method interaction, which you can control through the existing <code>Window.addFlags()</code> method and new <code>Window.setSoftInputMode()</code> method. The <code>PopupWindow</code>
+class has grown corresponding methods to control these options on its
+window. One thing in particular to be aware of is the new <code>WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM</code> constant, which is used to control whether a window is on top of or behind the current IME.</p>
+
+<p>Most of the interaction between an active IME and application is done through the <code>android.view.inputmethod.InputConnection</code>
+class. This is the API an application implement, which an IME calls to
+perform the appropriate edit operations on the application. You won't
+normally need to worry about this, since <code>TextView</code> provides its own implementation for itself.</p>
+
+<p>There are also a handful of new <code>View</code> APIs, the most important of these being<code> onCreateInputConnection()</code> which creates a new <code>InputConnection</code> for an IME (and fills in an <code>android.view.inputmethod.EditorInfo</code>
+structure with your input type, IME options, and other data); again,
+most developers won't need to worry about this, since TextView takes
+care of it for you.</p>
\ No newline at end of file
diff --git a/docs/html/resources/articles/painless-threading.jd b/docs/html/resources/articles/painless-threading.jd
new file mode 100644
index 0000000..921f4df
--- /dev/null
+++ b/docs/html/resources/articles/painless-threading.jd
@@ -0,0 +1,147 @@
+page.title=Painless Threading
+@jd:body
+
+<p>This article discusses the threading model used by Android applications and how applications can ensure best UI performance by spawning worker threads to handle long-running operations, rather than handling them in the main thread. The article also explains the API that your application can use to interact with Android UI toolkit components running on the main thread and spawn managed worker threads. </p>
+
+<h3>The UI thread</h3>
+
+<p>When an application is launched, the system creates a thread called
+"main" for the application. The main thread, also called the <em>UI
+thread</em>, is very important because it is in charge of dispatching the
+events to the appropriate widgets, including drawing events.
+It is also the thread where your application interacts with running
+components of the Android UI toolkit. </p>
+
+<p>For instance, if you touch the a button on screen, the UI thread dispatches
+the touch event to the widget, which in turn sets its pressed state and
+posts an invalidate request to the event queue. The UI thread dequeues
+the request and notifies the widget to redraw itself.</p>
+
+<p>This single-thread model can yield poor performance unless your application
+is implemented properly. Specifically, if everything is happening in a single
+thread, performing long operations such as network access or database
+queries on the UI thread will block the whole user interface. No event
+can be dispatched, including drawing events, while the long operation
+is underway. From the user's perspective, the application appears hung.
+Even worse, if the UI thread is blocked for more than a few seconds
+(about 5 seconds currently) the user is presented with the infamous "<a href="http://developer.android.com/guide/practices/design/responsiveness.html">application not responding</a>" (ANR) dialog.</p>
+
+<p>If you want to see how bad this can look, write a simple application
+with a button that invokes <code>Thread.sleep(2000)</code> in its
+<a href="http://developer.android.com/reference/android/view/View.OnClickListener.html">OnClickListener</a>.
+The button will remain in its pressed state for about 2 seconds before
+going back to its normal state. When this happens, it is very easy for
+the user to <em>perceive</em> the application as slow.</p>
+
+<p>To summarize, it's vital to the responsiveness of your application's UI to
+keep the UI thread unblocked. If you have long operations to perform, you should
+make sure to do them in extra threads (<em>background</em> or <em>worker</em>
+threads). </p>
+
+<p>Here's an example of a click listener downloading an image over the
+network and displaying it in an <a href="http://developer.android.com/reference/android/widget/ImageView.html">ImageView</a>:</p>
+
+<pre class="prettyprint">public void onClick(View v) {
+ new Thread(new Runnable() {
+ public void run() {
+ Bitmap b = loadImageFromNetwork();
+ mImageView.setImageBitmap(b);
+ }
+ }).start();
+}</pre>
+
+<p>At first, this code seems to be a good solution to your problem, as it does
+not block the UI thread. Unfortunately, it violates the single-threaded model
+for the UI: the Android UI toolkit is <em>not thread-safe</em> and must always
+be manipulated on the UI thread. In this piece of code above, the
+<code>ImageView</code> is manipulated on a worker thread, which can cause really
+weird problems. Tracking down and fixing such bugs can be difficult and
+time-consuming.</p>
+
+<p>Android offers several ways to access the UI
+thread from other threads. You may already be familiar with some of
+them but here is a comprehensive list:</p>
+
+<ul>
+<li>{@link android.app.Activity#runOnUiThread(java.lang.Runnable) Activity.runOnUiThread(Runnable)}</li>
+<li>{@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}</li>
+<li>{@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable, long)}</li>
+<li>{@link android.os.Handler}</li>
+</ul>
+
+<p>You can use any of these classes and methods to correct the previous code example:</p>
+
+<pre class="prettyprint">public void onClick(View v) {
+ new Thread(new Runnable() {
+ public void run() {
+ final Bitmap b = loadImageFromNetwork();
+ mImageView.post(new Runnable() {
+ public void run() {
+ mImageView.setImageBitmap(b);
+ }
+ });
+ }
+ }).start();
+}</pre>
+
+<p>Unfortunately,
+these classes and methods could also tend to make your code more complicated
+and more difficult to read. It becomes even worse when your implement
+complex operations that require frequent UI updates. </p>
+
+<p>To remedy this problem, Android 1.5 and later platforms offer a utility class
+called {@link android.os.AsyncTask}, that simplifies the creation of
+long-running tasks that need to communicate with the user interface.</p>
+
+<p>An <code>AsyncTask</code> equivalent is also available for applications that
+will run on Android 1.0 and 1.1. The name of the class is <a
+href="http://code.google.com/p/shelves/source/browse/trunk/Shelves/src/org/
+curiouscreature/android/shelves/util/UserTask.java">UserTask</a>. It offers the
+exact same API and all you have to do is copy its source code in your
+application.</p>
+
+<p>The goal of <code>AsyncTask</code> is to take care of thread management for
+you. Our previous example can easily be rewritten with
+<code>AsyncTask</code>:</p>
+
+<pre class="prettyprint">public void onClick(View v) {
+ new DownloadImageTask().execute("http://example.com/image.png");
+}
+
+private class DownloadImageTask extends AsyncTask<string, void,="" bitmap=""> {
+ protected Bitmap doInBackground(String... urls) {
+ return loadImageFromNetwork(urls[0]);
+ }
+
+ protected void onPostExecute(Bitmap result) {
+ mImageView.setImageBitmap(result);
+ }
+ }</pre>
+
+<p>As you can see, <code>AsyncTask</code> <em>must</em> be used by subclassing
+it. It is also very important to remember that an <code>AsyncTask</code>
+instance has to be created on the UI thread and can be executed only once. You
+can read the <a
+href="http://developer.android.com/reference/android/os/AsyncTask.html">
+AsyncTask documentation</a> for a full understanding on how to use this class,
+but here is a quick overview of how it works:</p>
+
+<ul>
+<li>You can specify the type, using generics, of the parameters, the progress values and the final value of the task</li>
+<li>The method <a href="http://developer.android.com/reference/android/os/AsyncTask.html#doInBackground%28Params...%29">doInBackground()</a> executes automatically on a worker thread</li>
+<li><a href="http://developer.android.com/reference/android/os/AsyncTask.html#onPreExecute%28%29">onPreExecute()</a>, <a href="http://developer.android.com/reference/android/os/AsyncTask.html#onPostExecute%28Result%29">onPostExecute()</a> and <a href="http://developer.android.com/reference/android/os/AsyncTask.html#onProgressUpdate%28Progress...%29">onProgressUpdate()</a> are all invoked on the UI thread</li>
+<li>The value returned by <a href="http://developer.android.com/reference/android/os/AsyncTask.html#doInBackground%28Params...%29">doInBackground()</a> is sent to <a href="http://developer.android.com/reference/android/os/AsyncTask.html#onPostExecute%28Result%29">onPostExecute()</a></li>
+<li>You can call <a href="http://developer.android.com/reference/android/os/AsyncTask.html#publishProgress%28Progress...%29">publishProgress()</a> at anytime in <a href="http://developer.android.com/reference/android/os/AsyncTask.html#doInBackground%28Params...%29">doInBackground()</a> to execute <a href="http://developer.android.com/reference/android/os/AsyncTask.html#onProgressUpdate%28Progress...%29">onProgressUpdate()</a> on the UI thread</li><li>You can cancel the task at any time, from any thread</li>
+</ul>
+
+<p>In addition to the official documentation, you can read several complex examples in the source code of Shelves (<a href="http://code.google.com/p/shelves/source/browse/trunk/Shelves/src/org/curiouscreature/android/shelves/activity/ShelvesActivity.java">ShelvesActivity.java</a> and <a href="http://code.google.com/p/shelves/source/browse/trunk/Shelves/src/org/curiouscreature/android/shelves/activity/AddBookActivity.java">AddBookActivity.java</a>) and Photostream (<a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/LoginActivity.java">LoginActivity.java</a>, <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/PhotostreamActivity.java">PhotostreamActivity.java</a> and <a href="http://code.google.com/p/apps-for-android/source/browse/trunk/Photostream/src/com/google/android/photostream/ViewPhotoActivity.java">ViewPhotoActivity.java</a>). We highly recommend reading the source code of <a href="http://code.google.com/p/shelves/">Shelves</a> to see how to persist tasks across configuration changes and how to cancel them properly when the activity is destroyed.</p>
+
+<p>Regardless of whether or not you use <a href="http://developer.android.com/reference/android/os/AsyncTask.html">AsyncTask</a>,
+always remember these two rules about the single thread model: </p>
+
+<ol>
+<li>Do not block the UI thread, and
+<li>Make sure that you access the Android UI toolkit <em>only</em> on the UI thread.
+</ol>
+
+<p><code>AsyncTask</code> just makes it easier to do both of these things.</p>
diff --git a/docs/html/resources/articles/qsb.jd b/docs/html/resources/articles/qsb.jd
new file mode 100644
index 0000000..1249f21
--- /dev/null
+++ b/docs/html/resources/articles/qsb.jd
@@ -0,0 +1,151 @@
+page.title=Quick Search Box
+@jd:body
+
+<img src="images/qsb_002.png" style="float: right; margin-left: 2em; margin-bottom: 1em; width: 233px; height: 349.5px;"></p>
+
+<p>Starting with Android 1.6, the platform includes support for Quick Search
+Box (QSB), a powerful, system-wide search framework. Quick Search Box makes it
+possible for users to quickly and easily find what they're looking for, both on
+their devices and on the web. It suggests content on your device as you type,
+like apps, contacts, browser history, and music. It also offers results from the
+web search suggestions, local business listings, and other info from
+Google, such as stock quotes, weather, and flight status. All of this is
+available right from the home screen, by tapping on Quick Search Box.</p>
+
+<p>What
+we're most excited about with this new feature is the ability for you,
+the developers, to leverage the QSB framework to provide quicker and
+easier access to the content inside your apps. Your apps can provide
+search suggestions that will surface to users in QSB alongside other
+search results and suggestions. This makes it possible for users to
+access your application's content from outside your application—for
+example, from the home screen.</p>
+
+<p class="note"><strong>Note:</strong> The code fragments in this document are
+related to a sample app called <a
+href="http://developer.android.com/guide/samples/SearchableDictionary/index.
+html" title="Searchable Dictionary">Searchable Dictionary</a>. The app is
+available for Android 1.6 and later platforms.</p>
+
+<h3>The story before now: searching within your app</h3>
+
+<p>Platform releases versions previous to Android 1.6 already provided a mechanism
+that let you expose search and search suggestions in your app, as described in
+the docs for {@link android.app.SearchManager}. That mechanism has not changed
+and requires the following two things in your
+<code>AndroidManifest.xml</code>:</p>
+
+<p>1) In your <code><activity></code>, an intent filter, and a reference
+to a <code>searchable.xml</code> file (described below):</p>
+
+<pre class="prettyprint"><intent-filter>
+ <action android:name="android.intent.action.SEARCH" />
+ <category android:name="android.intent.category.DEFAULT" />
+</intent-filter>
+
+<meta-data android:name="android.app.searchable"
+ android:resource="@xml/searchable" /></pre>
+
+<p>2) A content provider that can provide search suggestions according to the
+URIs and column formats specified by the
+<a href="{@docRoot}reference/android/app/SearchManager.html#Suggestions">Search Suggestions</a>
+section of the SearchManager docs:</p>
+
+<pre class="prettyprint"><!-- Provides search suggestions for words and their definitions. -->
+<provider android:name="DictionaryProvider"
+ android:authorities="dictionary"
+ android:syncable="false" /></pre>
+
+<p>In the <code>searchable.xml</code> file, you specify a few things about how
+you want the search system to present search for your app, including the
+authority of the content provider that provides suggestions for the user as they
+type. Here's an example of the <code>searchable.xml</code> of an Android app
+that provides search suggestions within its own activities:</p>
+
+<pre class="prettyprint"><searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/search_label"
+ android:searchSuggestAuthority="dictionary"
+ android:searchSuggestIntentAction="android.intent.action.VIEW">
+</searchable></pre>
+
+<p>Note that the <code>android:searchSuggestAuthority</code> attribute refers to
+the authority of the content provider we declared in
+<code>AndroidManifest.xml</code>.</p>
+
+<p>For more details on this, see the
+<a href="{@docRoot}reference/android/app/SearchManager.html#SearchabilityMetadata">Searchability Metadata
+section</a> of the of the SearchManager docs.</p>
+
+<h3>Including your app in Quick Search Box</h3>
+
+<div class="sidebox-wrapper">
+<div class="sidebox">
+<h2>Searchable Dictionary Sample App</h2>
+<p>Quick Search Box provides a really cool way to make it easier and faster for
+users to access your app's content. To help you get your app started with it,
+we've created a sample app that simply provides access to a small dictionary of
+words in QSB. The app is called Searchable Dictionary, and we encourage you to
+<a href="{@docRoot}resources/samples/SearchableDictionary/index.html">check it
+out</a>.</p>
+</div>
+</div>
+
+<p>In Android 1.6, we added a new attribute to the metadata for searchables:
+<code>android:includeInGlobalSearch</code>. By specifying this as
+<code>"true"</code> in your <code>searchable.xml</code>, you allow QSB to pick
+up your search suggestion content provider and include its suggestions along
+with the rest (if the user enables your suggestions from the system search
+settings).</p>
+
+<p>You should also specify a string value for
+<code>android:searchSettingsDescription</code>, which describes to users what
+sorts of suggestions your app provides in the system settings for search.</p>
+
+<pre class="prettyprint"><searchable xmlns:android="http://schemas.android.com/apk/res/android"
+ android:label="@string/search_label"
+ <span style="background: rgb(255, 255, 0) none repeat scroll 0% 0%; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;">android:searchSettingsDescription="@string/settings_description"</span>
+ <span style="background: rgb(255, 255, 0) none repeat scroll 0% 0%; -moz-background-clip: border; -moz-background-origin: padding; -moz-background-inline-policy: continuous;">android:includeInGlobalSearch="true"</span>
+ android:searchSuggestAuthority="dictionary"
+ android:searchSuggestIntentAction="android.intent.action.VIEW">
+</searchable></pre>
+
+<p>These new attributes are supported only in Android 1.6 and later.</p>
+
+<h3>What to expect</h3>
+
+<p>The
+first and most important thing to note is that when a user installs an
+app with a suggestion provider that participates in QSB, this new app
+will <em>not</em> be enabled for QSB by default. The user can choose
+to enable particular suggestion sources from the system settings for
+search (by going to "Search" > "Searchable items" in settings).</p>
+
+<p>You
+should consider how to handle this in your app. Perhaps show a notice
+that instructs the user to visit system settings and enable your app's
+suggestions.</p>
+
+<p>Once the user enables your searchable item, the
+app's suggestions will have a chance to show up in QSB, most likely
+under the "more results" section to begin with. As your app's
+suggestions are chosen more frequently, they can move up in the list.</p>
+
+<img src="images/qsb.png" style="width: 233px; height: 349.5px;">
+<img id="k0vw" src="images/qsb_003.png" style="width: 233px; height: 349.5px;">
+
+<h3>Shortcuts</h3>
+
+<p>One
+of our objectives with QSB is to make it faster for users to access the
+things they access most often. One way we've done this is by
+'shortcutting' some of the previously chosen search suggestions, so
+they will be shown immediately as the user starts typing, instead of
+waiting to query the content providers. Suggestions from your app may
+be chosen as shortcuts when the user clicks on them.</p>
+
+<p>For dynamic suggestions that may wish to change their content (or become invalid)
+in the future, you can provide a 'shortcut id'. This tells QSB to query
+your suggestion provider for up-to-date content for a suggestion after
+it has been displayed. For more details on how to manage shortcuts, see
+the Shortcuts section
+<a href="{@docRoot}reference/android/app/SearchManager.html#ExposingSearchSuggestionsToQuickSearchBox">within the SearchManager docs</a>.</p>
diff --git a/docs/html/resources/articles/timed-ui-updates.jd b/docs/html/resources/articles/timed-ui-updates.jd
new file mode 100644
index 0000000..863387c
--- /dev/null
+++ b/docs/html/resources/articles/timed-ui-updates.jd
@@ -0,0 +1,149 @@
+page.title=Updating the UI from a Timer
+@jd:body
+
+<img style="margin: 1.5em; float: right;" src="images/JFlubber.png" alt="" id="BLOGGER_PHOTO_ID_5135098660116808706" border="0">
+
+<p><strong>Background</strong>: While developing my first useful
+(though small) application for Android, which was a port of an existing
+utility I use when podcasting, I needed a way of updating a clock
+displayed on the UI at regular intervals, but in a lightweight and CPU
+efficient way.</p>
+
+<p><strong>Problem</strong>: In the original application I used
+java.util.Timer to update the clock, but that class is not such a good
+choice on Android. Using a Timer introduces a new thread into the
+application for a relatively minor reason. Thinking in terms of mobile
+applications often means re-considering choices that you might make
+differently for a desktop application with relatively richer resources
+at its disposal. We would like to find a more efficient way of updating
+that clock.</p>
+
+<p><strong>The Application</strong>: The original application is a
+Java Swing and SE application. It is like a stopwatch with a lap timer
+that we use when recording podcasts; when you start the recording, you
+start the stopwatch. Then for every mistake that someone makes, you hit
+the flub button. At the end you can save out the bookmarked mistakes
+which can be loaded into the wonderful
+<a href="http://audacity.sourceforge.net/" title="Audacity">Audacity</a>
+audio editor as a labels track. You can then see where all of the mistakes
+are in the recording and edit them out.</p>
+
+<p>The article describing it is: <a href="http://www.developer.com/java/ent/print.php/3589961" title="http://www.developer.com/java/ent/print.php/3589961">http://www.developer.com/java/ent/print.php/3589961</a></p>
+
+<p>In the original version, the timer code looked like this:</p>
+
+<pre>class UpdateTimeTask extends TimerTask {
+ public void run() {
+ long millis = System.currentTimeMillis() - startTime;
+ int seconds = (int) (millis / 1000);
+ int minutes = seconds / 60;
+ seconds = seconds % 60;
+
+ timeLabel.setText(String.format("%d:%02d", minutes, seconds));
+ }
+}</pre><p>And in the event listener to start this update, the following Timer() instance is used:
+</p><pre>if(startTime == 0L) {
+ startTime = evt.getWhen();
+ timer = new Timer();
+ timer.schedule(new UpdateTimeTask(), 100, 200);
+}</pre>
+
+<p>In particular, note the 100, 200 parameters. The first parameter
+means wait 100 ms before running the clock update task the first time.
+The second means repeat every 200ms after that, until stopped. 200 ms
+should not be too noticeable if the second resolution happens to fall
+close to or on the update. If the resolution was a second, you could
+find the clock sometimes not updating for close to 2 seconds, or
+possibly skipping a second in the counting, it would look odd).</p>
+
+<p>When I ported the application to use the Android SDKs, this code
+actually compiled in Eclipse, but failed with a runtime error because
+the Timer() class was not available at runtime (fortunately, this was
+easy to figure out from the error messages). On a related note, the
+String.format method was also not available, so the eventual solution
+uses a quick hack to format the seconds nicely as you will see.</p>
+
+<p>Fortunately, the role of Timer can be replaced by the
+android.os.Handler class, with a few tweaks. To set it up from an event
+listener:</p>
+
+<pre>private Handler mHandler = new Handler();
+
+...
+
+OnClickListener mStartListener = new OnClickListener() {
+ public void onClick(View v) {
+ if (mStartTime == 0L) {
+ mStartTime = System.currentTimeMillis();
+ mHandler.removeCallbacks(mUpdateTimeTask);
+ mHandler.postDelayed(mUpdateTimeTask, 100);
+ }
+ }
+};</pre>
+
+<p>A couple of things to take note of here. First, the event doesn't
+have a .getWhen() method on it, which we handily used to set the start
+time for the timer. Instead, we grab the System.currentTimeMillis().
+Also, the Handler.postDelayed() method only takes one time parameter,
+it doesn't have a "repeating" field. In this case we are saying to the
+Handler "call mUpdateTimeTask() after 100ms", a sort of fire and forget
+one time shot. We also remove any existing callbacks to the handler
+before adding the new handler, to make absolutely sure we don't get
+more callback events than we want.</p>
+
+<p>But we want it to repeat, until we tell it to stop. To do this, just
+put another postDelayed at the tail of the mUpdateTimeTask run()
+method. Note also that Handler requires an implementation of Runnable,
+so we change mUpdateTimeTask to implement that rather than extending
+TimerTask. The new clock updater, with all these changes, looks like
+this:</p>
+
+<pre>private Runnable mUpdateTimeTask = new Runnable() {
+ public void run() {
+ final long start = mStartTime;
+ long millis = SystemClock.uptimeMillis() - start;
+ int seconds = (int) (millis / 1000);
+ int minutes = seconds / 60;
+ seconds = seconds % 60;
+
+ if (seconds < 10) {
+ mTimeLabel.setText("" + minutes + ":0" + seconds);
+ } else {
+ mTimeLabel.setText("" + minutes + ":" + seconds);
+ }
+
+ mHandler.postAtTime(this,
+ start + (((minutes * 60) + seconds + 1) * 1000));
+ }
+};</pre>
+
+<p>and can be defined as a class member field.</p>
+
+<p>The if statement is just a way to make sure the label is set to
+10:06 instead of 10:6 when the seconds modulo 60 are less than 10
+(hopefully String.format() will eventually be available). At the end of
+the clock update, the task sets up another call to itself from the
+Handler, but instead of a hand-wavy 200ms before the update, we can
+schedule it to happen at a particular wall-clock time — the line: start
++ (((minutes * 60) + seconds + 1) * 1000) does this.</p>
+
+<p>All we need now is a way to stop the timer when the stop button
+is pressed. Another button listener defined like this:</p>
+
+<pre>OnClickListener mStopListener = new OnClickListener() {
+ public void onClick(View v) {
+ mHandler.removeCallbacks(mUpdateTimeTask);
+ }
+};</pre>
+
+<p>will make sure that the next callback is removed when the stop button
+is pressed, thus interrupting the tail iteration.</p>
+
+<p>Handler is actually a better choice than Timer for another reason
+too. The Handler runs the update code as a part of your main thread,
+avoiding the overhead of a second thread and also making for easy
+access to the View hierarchy used for the user interface. Just remember
+to keep such tasks small and light to avoid slowing down the user
+experience.</p>
+
+
diff --git a/docs/html/resources/articles/touch-mode.jd b/docs/html/resources/articles/touch-mode.jd
new file mode 100644
index 0000000..e340062
--- /dev/null
+++ b/docs/html/resources/articles/touch-mode.jd
@@ -0,0 +1,138 @@
+page.title=Touch Mode
+@jd:body
+
+<p>This article explains the <em>touch mode</em>, one of the most
+important principles of Android's UI toolkit.</p>
+
+<p>The touch mode is a state of the view hierarchy that depends solely on the
+user interaction with the phone. By itself, the touch mode is something very
+easy to understand as it simply indicates whether the last user interaction was
+performed with the touch screen. For example, if you are using an
+Android-powered device, selecting a widget with the trackball will take you out
+of touch mode; however, if you touch a button on the screen with your finger,
+you will enter touch mode. When the user is not in touch mode, we talk about the
+trackball mode, navigation mode or keyboard navigation, so do not be surprised
+if you encounter these terms. </p>
+
+<p>There is only one API directly related to touch mode,
+{@link android.view.View#isInTouchMode() View.isInTouchMode()}.</p>
+
+<p>Sounds easy enough, right? Oddly enough, touch mode is deceivingly simple and
+the consequences of entering touch mode are far greater than you might
+think. Let's look at some of the reasons why.</p>
+
+<h4>Touch Mode, Selection, and Focus</h4>
+
+<p>Designing a UI toolkit for mobile devices is difficult because of the various
+interaction mechanisms they provide. Some devices offer only 12 keys, some have
+a touch screen, some require a stylus, some have both a touch screen and a
+keyboard. Based on the hardware capabilities of the he user can interact with
+your application using different mechanisms, so we had to think very hard about
+all the possible issues that could arise. One issue led us to create the touch
+mode.</p>
+
+<p>Imagine a simple application, <a href="{@docRoot}resources/samples/index.html">ApiDemos</a>
+for example, that shows a list of text items. The user can freely
+navigate through the list using the trackball but also, alternatively, scroll
+and fling the list using the touch screen. The issue in this scenario is
+how to handle the selection properly when the user manipulates the list
+through the touch screen. </p>
+
+<p>In this case, if the user selects an item at the top of the list and then
+flings the list towards the bottom, what should happen to the selection? Should
+it remain on the item and scroll off the screen? What should happen if the user
+then decided to move the selection with the trackball? Or worse, what should
+happen if the user presses the trackball to act upon the currently selected
+item, which is not shown on screen anymore? </p>
+
+<p>After careful consideration, we decided to remove the selection altogether,
+when the user manipulates the UI through the touch screen.</p>
+
+<p>In touch mode, there is no focus and no selection. Any selected item in a
+list of in a grid becomes unselected as soon as the user enters touch
+mode. Similarly, any focused widgets become unfocused when the user
+enters touch mode. The image below illustrates what happens when the
+user touches a list after selecting an item with the trackball.</p>
+
+<img style="margin: 0px 7px;" src="images/list02.png" alt="" id="BLOGGER_PHOTO_ID_5272753165743060962" border="0">
+<img style="margin: 0px 7px;" src="images/list01.png" alt="" id="BLOGGER_PHOTO_ID_5272753357441963442" border="0">
+
+<p>To
+make things more natural for the user, the framework knows how to
+resurrect the selection/focus whenever the user leaves touch mode. For
+instance, in the example above, if the user were to use the trackball
+again, the selection would reappear on the previously-selected item.
+This is why some developers are confused when they create a custom view
+and start receiving key events only after moving the trackball once:
+their application is in touch mode, and they need to use the trackball
+to exit touch mode and resurrect the focus.</p>
+
+<p>The relationship between touch mode, selection, and focus means you must not
+rely on selection and/or focus to exist in your application. A very common
+problem with new Android developers is to rely on
+{@link android.widget.AdapterView#getSelectedItemPosition() ListView.getSelectedItemPosition()}.
+In touch mode, this method will return
+{@link android.widget.AdapterView#INVALID_POSITION INVALID_POSITION}.
+ You should instead use click listeners (see
+{@link android.widget.AdapterView#setOnItemClickListener(android.widget.AdapterView.OnItemClickListener)})
+or the choice mode (see
+{@link android.widget.ListView#setChoiceMode(int)}).</p>
+
+<h4>Focusable in Touch Mode</h4>
+
+<p>In general, focus doesn't exist in touch mode. However, focus can exist in
+touch mode in a very special way called <em>focusable</em>. This special mode
+was created for widgets that receive text input, such as
+{@link android.widget.EditText} or, when filtering is enabled,
+{@link android.widget.ListView}. The focusable mode is what lets the user enter text
+inside a text field on the screen, without first selecting it with the trackball
+or their finger.</p>
+
+<p>When a user
+touches the screen, the application will enter touch mode if it wasn't
+in touch mode already. What happens during the transition to
+touch mode depends on what the user touched, and what currently has
+focus. If the user touches a widget that is focusable in touch
+mode, that widget will receive focus. Otherwise, any currently
+focused widget will not retain focus unless it is focusable in touch
+mode. For instance, in the picture below, when the user touches
+the screen, the input text field receives the focus.</p>
+
+<img style="margin: 0px 7px;" src="images/text_field.png" alt="" id="BLOGGER_PHOTO_ID_5272755475757779154" border="0">
+
+<p>Fousable in touch mode (see
+{@link android.view.View#setFocusableInTouchMode(boolean) View.setFocusableInTouchMode})
+ is a property that you can set yourself, either from code or from XML.
+However, you should use it sparingly and only in very specific situations,
+because it breaks consistency with the normal behavior of the Android UI. A game
+is a good example of an application that could make good use of the focusable in
+touch mode property. MapView, if used in fullscreen as in Google Maps, is
+another good example of where you can use focusable in touch mode correctly.</p>
+
+<p>Below is another example of a focusable in touch mode widget. When the user
+taps an <code>AutoCompleteTextView</code> suggestion with his finger, the focus
+remains on the input text field:</p>
+
+<img style="margin: 0px 7px;" src="images/search01.png" alt="" id="BLOGGER_PHOTO_ID_5272756689821626962" border="0">
+<img style="margin: 0px 7px;" src="images/search02.png" alt="" id="BLOGGER_PHOTO_ID_5272756246104676754" border="0">
+
+<p>Developers new to Android often think that focusable in touch mode is the
+solution they need to "fix" the problem of "disappearing" selection/focus. We
+really encourage you to think very hard before using it. If used incorrectly, it
+can make your application behave differently from the rest of the system and
+simply throw off the user's habits. The Android framework contains all the tools
+you need to handle user interactions without using focusable in touch mode. For
+example, instead of trying to make <code>ListView</code> always keep its
+selection, simply use the appropriate choice mode, as shown in
+{@link android.widget.ListView#setChoiceMode(int)}.
+
+<h4>Touch Mode Cheat Sheet</h4>
+
+<p>Do:</p>
+<ul>
+<li>Remain consistent with the core applications</li><li>Use the appropriate feature if you need persistent selection (radio button, check box, the <code>ListView</code> choice mode, etc.)</li>
+<li>Use focusable in touch mode if you write a game</li>
+</ul>
+
+<p>Don't:</p>
+<ul><li>Do not try to keep the focus or selection in touch mode</li></ul>
diff --git a/docs/html/resources/articles/track-mem.jd b/docs/html/resources/articles/track-mem.jd
new file mode 100644
index 0000000..d580e82
--- /dev/null
+++ b/docs/html/resources/articles/track-mem.jd
@@ -0,0 +1,62 @@
+page.title=Tracking Memory Allocations
+@jd:body
+
+<p>Writing efficient mobile applications is not always straightforward. In
+particular, Android applications rely on automatic memory management handled by
+Dalvik's garbage collector, which can sometimes cause performance issues if you
+are not careful with memory allocations.</p>
+
+<p>In a performance-sensitive code path, such as the layout or drawing method of
+a view or the logic code of a game, any allocation comes at a price. After too
+many allocations, the garbage collector will kick in and stop your application
+to let it free some memory. Most of the time, garbage collections happen fast
+enough for you not to notice. However, if a collection happens while you are
+scrolling through a list of items or while you are trying to defeat a foe in a
+game, you may suddenly see a drop in performance/responsiveness of the
+application. It's not unusual for a garbage collection to take 100 to 200 ms.
+For comparison, a smooth animation needs to draw each frame in 16 to 33 ms. If
+the animation is suddenly interrupted for 10 frames, you can be certain that
+your users will notice.</p>
+
+<p>Most of the time, garbage collection occurs because of tons of small,
+short-lived objects and some garbage collectors, like generational garbage
+collectors, can optimize the collection of these objects so that the application
+does not get interrupted too often. The Android garbage collector is
+unfortunately not able to perform such optimizations and the creation of
+short-lived objects in performance critical code paths is thus very costly for
+your application.</p>
+
+<p>To help you avoid frequent garbage collections, the Android SDK ships with a
+very useful tool called <em>allocation tracker</em>. This tool is part of DDMS,
+which you must have already used for debugging purposes. To start using the
+allocation tracker, you must first launch the standalone version of DDMS, which
+can be found in the <code>tools/</code> directory of the SDK. The version of
+DDMS included in the Eclipse plugin does not offer you ability to use the
+allocation tracker yet.</p>
+
+<p>Once DDMS is running, simply select your application process and then click
+the <em>Allocation Tracker</em> tab. In the new view, click <em>Start
+Tracking</em> and then use your application to make it execute the code paths
+you want to analyze. When you are ready, click <em>Get Allocations</em>. A list
+of allocated objects will be shown in the first table. By clicking on a line you
+can see, in the second table, the stack trace that led to the allocation. Not
+only you will know what type of object was allocated, but also in which thread,
+in which class, in which file and at which line. The following screenshot shows
+the allocations performed by <a
+href="http://code.google.com/p/shelves">Shelves</a> while scrolling a
+ListView.</p>
+
+<a href="images/ddms_allocation_trackerl.png">
+
+<img style="cursor:hand;width: 320px; height: 250px;" src="images/ddms_allocation_tracker.png" border="0" alt="" />
+</a>
+
+<p>Even though it is not necessary — and sometimes not possible — to
+remove all allocations for your performance critical code paths. the allocation
+tracker will help you identify important issues in your code. For instance, a
+common mistake I have seen in many applications is to create a new
+<code>Paint</code> object on every draw. Moving the paint into an instance field
+is a simple fix that helps performance a lot. I highly encourage you to peruse
+the <a href="http://source.android.com/">Android source code</a> to see how we
+reduce allocations in performance-critical code paths. You will also thus
+discover the APIs Android provide to help you reuse objects.</p>
diff --git a/docs/html/resources/articles/tts.jd b/docs/html/resources/articles/tts.jd
new file mode 100644
index 0000000..e3fad91
--- /dev/null
+++ b/docs/html/resources/articles/tts.jd
@@ -0,0 +1,241 @@
+page.title=Using Text-to-Speech
+@jd:body
+
+<p>Starting with Android 1.6 (API Level 4), the Android platform includes a new
+Text-to-Speech (TTS) capability. Also known as "speech synthesis", TTS enables
+your Android device to "speak" text of different languages.</p>
+
+<p>Before we explain how to use the TTS API itself, let's first review a few
+aspects of the engine that will be important to your TTS-enabled application. We
+will then show how to make your Android application talk and how to configure
+the way it speaks.</p>
+
+<h3>Languages and resources</h3>
+
+<p>The TTS engine that ships with the Android platform supports a number of
+languages: English, French, German, Italian and Spanish. Also, depending on
+which side of the Atlantic you are on, American and British accents for English
+are both supported.</p>
+
+<p>The TTS engine needs to know which language to speak, as a word like "Paris",
+for example, is pronounced differently in French and English. So the voice and
+dictionary are language-specific resources that need to be loaded before the
+engine can start to speak.</p>
+
+<p>Although all Android-powered devices that support the TTS functionality ship
+with the engine, some devices have limited storage and may lack the
+language-specific resource files. If a user wants to install those resources,
+the TTS API enables an application to query the platform for the availability of
+language files and can initiate their download and installation. So upon
+creating your activity, a good first step is to check for the presence of the
+TTS resources with the corresponding intent:</p>
+
+<pre>Intent checkIntent = new Intent();
+checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
+startActivityForResult(checkIntent, MY_DATA_CHECK_CODE);</pre>
+
+<p>A successful check will be marked by a <code>CHECK_VOICE_DATA_PASS</code>
+result code, indicating this device is ready to speak, after the creation of
+our
+{@link android.speech.tts.TextToSpeech} object. If not, we need to let the user
+know to install the data that's required for the device to become a
+multi-lingual talking machine! Downloading and installing the data is
+accomplished by firing off the ACTION_INSTALL_TTS_DATA intent, which will take
+the user to Android Market, and will let her/him initiate the download.
+Installation of the data will happen automatically once the download completes.
+Here is an example of what your implementation of
+<code>onActivityResult()</code> would look like:</p>
+
+<pre>private TextToSpeech mTts;
+protected void onActivityResult(
+ int requestCode, int resultCode, Intent data) {
+ if (requestCode == MY_DATA_CHECK_CODE) {
+ if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
+ // success, create the TTS instance
+ mTts = new TextToSpeech(this, this);
+ } else {
+ // missing data, install it
+ Intent installIntent = new Intent();
+ installIntent.setAction(
+ TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
+ startActivity(installIntent);
+ }
+ }
+}</pre>
+
+<p>In the constructor of the <code>TextToSpeech</code> instance we pass a
+reference to the <code>Context</code> to be used (here the current Activity),
+and to an <code>OnInitListener</code> (here our Activity as well). This listener
+enables our application to be notified when the Text-To-Speech engine is fully
+loaded, so we can start configuring it and using it.</p>
+
+<h4>Languages and Locale</h4>
+
+<p>At Google I/O 2009, we showed an <a title="Google I/O 2009, TTS
+demonstration" href="http://www.youtube.com/watch?v=uX9nt8Cpdqg#t=6m17s"
+id="rnfd">example of TTS</a> where it was used to speak the result of a
+translation from and to one of the 5 languages the Android TTS engine currently
+supports. Loading a language is as simple as calling for instance:</p>
+
+<pre>mTts.setLanguage(Locale.US);</pre><p>to load and set the language to
+English, as spoken in the country "US". A locale is the preferred way to specify
+a language because it accounts for the fact that the same language can vary from
+one country to another. To query whether a specific Locale is supported, you can
+use <code>isLanguageAvailable()</code>, which returns the level of support for
+the given Locale. For instance the calls:</p>
+
+<pre>mTts.isLanguageAvailable(Locale.UK))
+mTts.isLanguageAvailable(Locale.FRANCE))
+mTts.isLanguageAvailable(new Locale("spa", "ESP")))</pre>
+
+<p>will return TextToSpeech.LANG_COUNTRY_AVAILABLE to indicate that the language
+AND country as described by the Locale parameter are supported (and the data is
+correctly installed). But the calls:</p>
+
+<pre>mTts.isLanguageAvailable(Locale.CANADA_FRENCH))
+mTts.isLanguageAvailable(new Locale("spa"))</pre>
+
+<p>will return <code>TextToSpeech.LANG_AVAILABLE</code>. In the first example,
+French is supported, but not the given country. And in the second, only the
+language was specified for the Locale, so that's what the match was made on.</p>
+
+<p>Also note that besides the <code>ACTION_CHECK_TTS_DATA</code> intent to check
+the availability of the TTS data, you can also use
+<code>isLanguageAvailable()</code> once you have created your
+<code>TextToSpeech</code> instance, which will return
+<code>TextToSpeech.LANG_MISSING_DATA</code> if the required resources are not
+installed for the queried language.</p>
+
+<p>Making the engine speak an Italian string while the engine is set to the
+French language will produce some pretty <i>interesting </i>results, but it will
+not exactly be something your user would understand So try to match the
+language of your application's content and the language that you loaded in your
+<code>TextToSpeech</code> instance. Also if you are using
+<code>Locale.getDefault()</code> to query the current Locale, make sure that at
+least the default language is supported.</p>
+
+<h3>Making your application speak</h3>
+
+<p>Now that our <code>TextToSpeech</code> instance is properly initialized and
+configured, we can start to make your application speak. The simplest way to do
+so is to use the <code>speak()</code> method. Let's iterate on the following
+example to make a talking alarm clock:</p>
+
+<pre>String myText1 = "Did you sleep well?";
+String myText2 = "I hope so, because it's time to wake up.";
+mTts.speak(myText1, TextToSpeech.QUEUE_FLUSH, null);
+mTts.speak(myText2, TextToSpeech.QUEUE_ADD, null);</pre>
+
+<p>The TTS engine manages a global queue of all the entries to synthesize, which
+are also known as "utterances". Each <code>TextToSpeech</code> instance can
+manage its own queue in order to control which utterance will interrupt the
+current one and which one is simply queued. Here the first <code>speak()</code>
+request would interrupt whatever was currently being synthesized: the queue is
+flushed and the new utterance is queued, which places it at the head of the
+queue. The second utterance is queued and will be played after
+<code>myText1</code> has completed.</p>
+
+<h4>Using optional parameters to change the playback stream type</h4>
+
+<p>On Android, each audio stream that is played is associated with one stream
+type, as defined in
+{@link android.media.AudioManager android.media.AudioManager}. For a talking
+alarm clock, we would like our text to be played on the
+<code>AudioManager.STREAM_ALARM</code> stream type so that it respects the alarm
+settings the user has chosen on the device. The last parameter of the speak()
+method allows you to pass to the TTS engine optional parameters, specified as
+key/value pairs in a HashMap. Let's use that mechanism to change the stream type
+of our utterances:</p>
+
+<pre>HashMap<String, String> myHashAlarm = new HashMap();
+myHashAlarm.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
+ String.valueOf(AudioManager.STREAM_ALARM));
+mTts.speak(myText1, TextToSpeech.QUEUE_FLUSH, myHashAlarm);
+mTts.speak(myText2, TextToSpeech.QUEUE_ADD, myHashAlarm);</pre>
+
+<h4>Using optional parameters for playback completion callbacks</h4>
+
+<p>Note that <code>speak()</code> calls are asynchronous, so they will return
+well before the text is done being synthesized and played by Android, regardless
+of the use of <code>QUEUE_FLUSH</code> or <code>QUEUE_ADD</code>. But you might
+need to know when a particular utterance is done playing. For instance you might
+want to start playing an annoying music after <code>myText2</code> has finished
+synthesizing (remember, we're trying to wake up the user). We will again use an
+optional parameter, this time to tag our utterance as one we want to identify.
+We also need to make sure our activity implements the
+<code>TextToSpeech.OnUtteranceCompletedListener</code> interface:</p>
+
+<pre>mTts.setOnUtteranceCompletedListener(this);
+myHashAlarm.put(TextToSpeech.Engine.KEY_PARAM_STREAM,
+ String.valueOf(AudioManager.STREAM_ALARM));
+mTts.speak(myText1, TextToSpeech.QUEUE_FLUSH, myHashAlarm);
+myHashAlarm.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID,
+ "end of wakeup message ID");
+// myHashAlarm now contains two optional parameters
+mTts.speak(myText2, TextToSpeech.QUEUE_ADD, myHashAlarm);</pre>
+
+<p>And the Activity gets notified of the completion in the implementation
+of the listener:</p>
+
+<pre>public void onUtteranceCompleted(String uttId) {
+ if (uttId == "end of wakeup message ID") {
+ playAnnoyingMusic();
+ }
+}</pre>
+
+<h4>File rendering and playback</h4>
+
+<p>While the <code>speak()</code> method is used to make Android speak the text
+right away, there are cases where you would want the result of the synthesis to
+be recorded in an audio file instead. This would be the case if, for instance,
+there is text your application will speak often; you could avoid the synthesis
+CPU-overhead by rendering only once to a file, and then playing back that audio
+file whenever needed. Just like for <code>speak()</code>, you can use an
+optional utterance identifier to be notified on the completion of the synthesis
+to the file:</p>
+
+<pre>HashMap<String, String> myHashRender = new HashMap();
+String wakeUpText = "Are you up yet?";
+String destFileName = "/sdcard/myAppCache/wakeUp.wav";
+myHashRender.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, wakeUpText);
+mTts.synthesizeToFile(wakuUpText, myHashRender, destFileName);</pre>
+
+<p>Once you are notified of the synthesis completion, you can play the output
+file just like any other audio resource with
+{@link android.media.MediaPlayer android.media.MediaPlayer}.</p>
+
+<p>But the <code>TextToSpeech</code> class offers other ways of associating
+audio resources with speech. So at this point we have a WAV file that contains
+the result of the synthesis of "Wake up" in the previously selected language. We
+can tell our TTS instance to associate the contents of the string "Wake up" with
+an audio resource, which can be accessed through its path, or through the
+package it's in, and its resource ID, using one of the two
+<code>addSpeech()</code> methods:</p>
+
+<pre>mTts.addSpeech(wakeUpText, destFileName);</pre>
+
+<p>This way any call to speak() for the same string content as
+<code>wakeUpText</code> will result in the playback of
+<code>destFileName</code>. If the file is missing, then speak will behave as if
+the audio file wasn't there, and will synthesize and play the given string. But
+you can also take advantage of that feature to provide an option to the user to
+customize how "Wake up" sounds, by recording their own version if they choose
+to. Regardless of where that audio file comes from, you can still use the same
+line in your Activity code to ask repeatedly "Are you up yet?":</p>
+
+<pre>mTts.speak(wakeUpText, TextToSpeech.QUEUE_ADD, myHashAlarm);</pre>
+
+<h4>When not in use...</h4><p>The text-to-speech functionality relies on a
+dedicated service shared across all applications that use that feature. When you
+are done using TTS, be a good citizen and tell it "you won't be needing its
+services anymore" by calling <code>mTts.shutdown()</code>, in your Activity
+<code>onDestroy()</code> method for instance.</p>
+
+<h3>Conclusion</h3>
+
+<p>Android now talks, and so can your apps. Remember that in order for
+synthesized speech to be intelligible, you need to match the language you select
+to that of the text to synthesize. Text-to-speech can help you push your app in
+new directions. Whether you use TTS to help users with disabilities, to enable
+the use of your application while looking away from the screen, or simply to
+make it cool, we hope you'll enjoy this new feature.</p>
\ No newline at end of file
diff --git a/docs/html/resources/articles/ui-1.5.jd b/docs/html/resources/articles/ui-1.5.jd
new file mode 100644
index 0000000..c10cf52
--- /dev/null
+++ b/docs/html/resources/articles/ui-1.5.jd
@@ -0,0 +1,48 @@
+page.title=UI Framework Changes in Android 1.5
+@jd:body
+
+
+<p>Android 1.5 offers a different default look for
+the Android UI framework, in relation to Android 1.0 and 1.1. The
+screenshots below show the same activity (creating a new contact) on
+Android 1.1 and Android 1.5:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 277px;" src="images/android.png" alt="" id="BLOGGER_PHOTO_ID_5323141124169754690" border="0">
+
+<p>You
+can see in this example that the buttons and checkboxes have a new
+appearance. Even though these changes do not affect binary nor source
+compatibility, they might still break the UI of your apps. As part of
+the UI refresh, the minimum size of some of the widgets has changed.
+For instance, Android 1.1 buttons have a minimum size of 44x48 pixels
+whereas Android 1.5 buttons now have a minimum size of 24x48 pixels.
+The image below compares the sizes of Android 1.1 buttons with Android
+1.5 buttons:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 100px; height: 130px;" src="images/buttons.png" alt="" border="0">
+
+<p>If you rely on the button's minimum size, then the layout of your application
+may not be the same in Android 1.5 as it was in Android 1.1 because of this
+change. This would happen for instance if you created a grid of buttons using
+<code>LinearLayout</code> and relying on the minimum size yielded by
+<code>wrap_content</code> to align the buttons properly:</p>
+
+<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 277px;" src="images/grid.png" alt="" id="BLOGGER_PHOTO_ID_5323147943613255250" border="0">
+
+<p>This layout could easily be fixed by using the
+<code>android:layout_weight</code> attribute or by replacing the
+<code>LinearLayout</code> containers with a <code>TableLayout</code>.</p>
+
+<p>This example is probably the worst-case UI issue you may encounter when
+running your application on Android 1.5. Other changes introduced in Android
+1.5, especially bug fixes in the layout views, may also impact your
+application—especially if it is relying on faulty/buggy behavior of the UI
+framework.</p>
+
+<p>If you encounter issues when running your application on Android 1.5, please
+join us on the <a href="{@docRoot}resources/community-groups.html">Android
+developer groups</a> or <a
+href="{@docRoot}resources/community-more.html">IRC</a> so that we and the
+Android community can help you fix your application.</p>
+
+<p>Happy coding!</p>
diff --git a/docs/html/resources/articles/ui-1.6.jd b/docs/html/resources/articles/ui-1.6.jd
new file mode 100644
index 0000000..10cb524
--- /dev/null
+++ b/docs/html/resources/articles/ui-1.6.jd
@@ -0,0 +1,130 @@
+page.title=UI Framework Changes in Android 1.6
+@jd:body
+
+<p>Android 1.6 introduces numerous enhancements and bug fixes in the UI
+framework. This article highlights two improvements in particular: more flexible
+and robust RelativeLayout and easier click listeners. </p>
+
+<h3>More flexible, more robust RelativeLayout</h3>
+
+<p>RelativeLayout is the most versatile layout offered by the Android UI toolkit
+and can be successfully used to reduce the number of views created by your
+applications. This layout used to suffer from various bugs and limitations,
+sometimes making it difficult to use without having some knowledge of its
+implementation. To make your life easier, Android 1.6 comes with a revamped
+RelativeLayout. </p>
+
+<p>This new implementation not only fixes all known bugs in
+RelativeLayout but also addresses its major limitation: the
+fact that views had to be declared in a particular order. Consider the following
+XML layout:</p>
+
+<pre><?xml version="1.0" encoding="utf-8"?>
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="64dip"
+ android:padding="6dip">
+
+ <TextView
+ android:id="@+id/band"
+ android:layout_width="fill_parent"
+ android:layout_height="26dip"
+
+ android:layout_below="@+id/track"
+ android:layout_alignLeft="@id/track"
+ android:layout_alignParentBottom="true"
+
+ android:gravity="top"
+ android:text="The Airborne Toxic Event" />
+
+ <TextView
+ android:id="@id/track"
+ android:layout_marginLeft="6dip"
+ android:layout_width="fill_parent"
+ android:layout_height="26dip"
+
+ android:layout_toRightOf="@+id/artwork"
+
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="bottom"
+ android:text="Sometime Around Midnight" />
+
+ <ImageView
+ android:id="@id/artwork"
+ android:layout_width="56dip"
+ android:layout_height="56dip"
+ android:layout_gravity="center_vertical"
+
+ android:src="@drawable/artwork" />
+
+</RelativeLayout></pre>
+
+<p>This code builds a very simple layout—an image on the left with two lines of
+text stacked vertically. This XML layout is perfectly fine and contains no
+errors. Unfortunately, Android 1.5's RelativeLayout is incapable of rendering it
+correctly, as shown in the screenshot below.</p>
+
+<img src="images/ui-1.6_002.png" style="width: 320px; height: 480px;">
+
+<p>The problem is that this layout uses forward references. For instance, the
+"band" TextView is positioned below the "track" TextView but "track" is declared
+after "band" and, in Android 1.5, RelativeLayout does not know how to handle
+this case. Now look at the exact same layout running on Android 1.6:</p>
+
+<img src="images/ui-1.6.png" style="width: 320px; height: 480px;">
+
+<p>As you can see Android 1.6 is now better able to handle forward reference.
+The result on screen is exactly what you would expect when writing the
+layout.</p>
+
+<h3>Easier click listeners</h3>
+
+<p>Setting up a click listener on a button is very common task, but
+it requires quite a bit of boilerplate code:</p>
+
+<pre>findViewById(R.id.myButton).setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ // Do stuff
+ }
+});</pre>
+
+<p>One way to reduce the amount of boilerplate is to share a single click
+listener between several buttons. While this technique reduces the
+number of classes, it still requires a fair amount of code and it still
+requires giving each button an id in your XML layout file:</p>
+
+<pre>View.OnClickListener handler = View.OnClickListener() {
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.myButton: // doStuff
+ break;
+ case R.id.myOtherButton: // doStuff
+ break;
+ }
+ }
+}
+
+findViewById(R.id.myButton).setOnClickListener(handler);
+findViewById(R.id.myOtherButton).setOnClickListener(handler);</pre>
+
+<p>With Android 1.6, none of this is necessary. All you have to do is
+declare a public method in your Activity to handle the click
+(the method <i>must</i> have one View argument):</p>
+
+<pre>class MyActivity extends Activity {
+ public void myClickHandler(View target) {
+ // Do stuff
+ }
+}</pre>
+
+<p>And then reference this method from your XML layout:</p>
+
+<pre><Button android:onClick="myClickHandler" /></pre>
+
+<p>This new feature reduces both the amount of Java and XML you have to write,
+leaving you more time to concentrate on your application.</p>
+
+<p>The Android team is committed to helping you write applications in the
+easiest and most efficient way possible. We hope you find these improvements
+useful and we're excited to see your applications on Android Market.</p>
diff --git a/docs/html/resources/articles/using-webviews.jd b/docs/html/resources/articles/using-webviews.jd
new file mode 100644
index 0000000..3a1f34c
--- /dev/null
+++ b/docs/html/resources/articles/using-webviews.jd
@@ -0,0 +1,61 @@
+page.title=Using WebViews
+@jd:body
+
+<p>A small application called <a title="WebViewDemo"
+href="http://code.google.com/p/apps-for-android/source/browse/#svn/trunk/Samples
+/WebViewDemo">WebViewDemo</a> shows how you can add web content to your
+application. You can find it in the <a title="apps-for-android"
+href="http://code.google.com/p/apps-for-android/">apps-for-android</a> project.
+This application demonstrates how you can embed a {@link android.webkit.WebView}
+into an activity and also how you can have two way communication between your
+application and the web content. </p>
+
+<p>A
+WebView uses the same rendering and JavaScript engine as the browser,
+but it runs under the control of your application. The WebView can be
+full screen or you can mix it with other Views. The content for your
+WebView can come from anywhere. The WebView can download content from
+the web, or it can come from local files stored in your assets
+directory. The content can even be dynamically generated by your
+application code. For this example, the HTML comes from a local file
+called <a title="demo.html" href="http://code.google.com/p/apps-for-android/source/browse/trunk/Samples/WebViewDemo/assets/demo.html">demo.html</a>.</p>
+
+<p>This application does not do very much: when you click on the
+android, he raises his arm.</p>
+
+<div style="text-align: center;"><img style="width: 322px; height: 482px;" src="images/webview.png"></div>
+
+<p>This
+could, of course, easily be accomplished with a little bit of
+JavaScript. Instead, though, WebViewDemo takes a slightly more
+complicated path to illustrate two very powerful features of WebView.</p>
+
+<p>First,
+JavaScript running inside the WebView can call out to code in your
+Activity. You can use this to have your JavaScript trigger actions like
+starting a new activity, or it can be used to fetch data from a
+database or {@link android.content.ContentProvider}. The API for this
+is very simple: just call the
+{@link android.webkit.WebView#addJavascriptInterface(java.lang.Object, java.lang.String) addJavascriptInterface()}
+method on your WebView. You pass an object whose methods you want to
+expose to JavaScript and the name to use when making calls. You can see
+the exact syntax in <a title="WebViewDemo.java"
+href="http://code.google.com/p/apps-for-android/source/browse/trunk/Samples/
+WebViewDemo/src/com/google/android/webviewdemo/WebViewDemo.java">WebViewDemo.
+java</a>. Here we are making our DemoJavascriptInterface object available to
+JavaScript where it will be called "window.demo".</p>
+
+<p>Second, your Activity can invoke JavaScript methods. All you have to do
+is call the {@link android.webkit.WebView#loadUrl(java.lang.String) loadUrl}
+method with the appropriate JavaScript call:</p>
+
+<p><code style="padding-left: 25px;">mWebView.loadUrl("javascript:wave()");</code></p>
+
+<p>Our <a title="WebViewDemo"
+href="http://code.google.com/p/apps-for-android/source/browse/#svn/trunk/Samples
+/WebViewDemo">WebViewDemo</a> uses both techniques: when you click on the
+android, it calls out to the activity, which then turns around and calls back
+into the JavaScript. WebViews are very powerful, and they may be a valuable tool
+to help you build your application – especially if you already have a lot of
+HTML content. As it happens, we've used exactly this approach in some of the
+applications we've written.</p>
diff --git a/docs/html/resources/articles/wikinotes-intents.jd b/docs/html/resources/articles/wikinotes-intents.jd
new file mode 100644
index 0000000..bc64544
--- /dev/null
+++ b/docs/html/resources/articles/wikinotes-intents.jd
@@ -0,0 +1,255 @@
+page.title=WikiNotes: Routing Intents
+@jd:body
+
+
+<p>In <a href="wikinotes-linkify.html">the Linkify! article</a>, we talked about
+using Linkify to turn wiki words (those that match a regular expression that we
+have defined) into a <code>content:</code> URI and defining a path to data that
+matched a note belonging to that wiki word. As an example, a matching word like
+<code>ToDoList</code> would be turned into a URI such as
+<code>content://com.google.android.wikinotes.db.wikinotes/wikinotes/ToDoList
+</code> and then acted upon using the VIEW action from the Linkify class.</p>
+
+<p>This article examines how the Android system takes this combination of
+<code>VIEW</code> action and <code>content:</code> URI and finds the correct
+activity to fire in order to do something with the data. It will also explain
+how the other default links created by Linkify, such as web URLs and telephone
+numbers, also result in the correct activity to handle that data type being
+fired. Finally, this article will start to examine the custom
+<code>ContentProvider</code> that has been created to handle WikiNotes data. The
+full description of the ContentProvider and what it does will span a couple more
+articles as well, because there is a lot to cover.</p>
+
+<h3>The Linkify-calls-intent Workflow</h3>
+
+<p>At a high level, the steps for Linkify to invoke an intent, and for the
+resulting activity (if any) to handle it, look like this:</p>
+
+<ol>
+ <li>Linkify is invoked on a TextView to turn matching text patterns into Intent links.</li>
+ <li>Linkify takes over monitoring for those Intent links being selected by the user.</li>
+ <li>When the user selects a link, Linkify calls the VIEW action using the content: URI associated with the link.</li>
+ <li>Android takes the content: URI that represents the data, and looks for a
+ContentProvider registered in the system that matches the URI.</li>
+ <li>If a match is found, Android queries the ContentProvider using the URI,
+and asks what MIME type the data that will be returned from the URI is.</li>
+ <li>Android then looks for an activity registered in the system with an
+intent-filter that matches both the VIEW action, and the MIME type for
+the data represented by the content: URI.</li>
+ <li>Assuming a match is found, Linkify then invokes the intent for
+the URI, at which point the activity takes over, and is handed
+the content: URI.</li>
+ <li>The activity can then use the URI to retrieve the data and act on
+it.</li>
+</ol>
+
+<p>This is actually a simpler process than it
+sounds, and it is quite lightweight as well. Perhaps a more
+understandable statement about how it works might be:</p>
+
+<p>Linkify is used to turn matching text into hot-links. When the user
+selects a hot-link, Android takes the data locator represented by the
+hot-link and looks for a data handler for that data locator. If it
+finds one, it asks for what type of data is returned for that locator.
+It then looks for something registered with the system that handles
+that type of data for the VIEW action, and starts it, including the
+data locator in the request.</p>
+
+<p>The real key here is the MIME type. MIME stands for <a
+href="http://en.wikipedia.org/wiki/MIME">Multipurpose Internet Mail
+Extensions</a> — a standard for sending attachments over email. The MIME
+type (which is the part Android uses) is a way of describing certain kinds of
+data. That type is then used to look for an Activity that can do something with
+that data type. In this way, ContentProviders and Activities (or other
+IntentReceivers) are decoupled, meaning that a given Content URI might have a
+different ContentProvider to handle it, but could still use the same MIME type
+meaning that the same activity could be called upon to handle the resulting
+data.</p>
+
+<h3>Linkify on a wiki word</h3>
+
+<p>Using the above workflow, let's take a look at exactly how the process
+works in WikiNotes for Android:</p>
+
+<p>First, Linkify is used to turn text matching the wiki word regular expression
+into a link that provides a Content URI for that wiki word, for example
+<code>content://com.google.android.wikinotes.db.wikinotes/wikinotes/ToDoList</code>.</p>
+
+<p>When the user clicks on the wiki word link, Linkify invokes the VIEW
+action on the Content URI. At this point, the Android system takes over
+getting the Intent request to the correct activity.</p>
+
+<p>Next, Android looks for a ContentProvider that has been registered
+with the system to handle URIs matching our Content URI format.</p>
+
+<p>In our case, we have a definition inside
+<a href="http://code.google.com/p/apps-for-android/source/browse/trunk/WikiNotes/AndroidManifest.xml">our application's AndroidManifest.xml</a>
+file that reads:</p>
+
+<pre><provider name="com.google.android.wikinotes.db.WikiNotesProvider"
+ android:authorities="com.google.android.wikinotes.db.wikinotes" /></pre>
+
+<p>This establishes that we have a ContentProvider defined in our application
+that provides the "root authority":
+<code>com.google.android.wikinotes.db.wikinotes</code>. This is the first part
+of the Content URI that we create for a wiki word link. Root Authority is just
+another way of thinking about a descriptor that is registered with Android to
+allow requests for certain URLs to be routed to the correct class.</p>
+
+<p>So, the whole definition is that a class called
+<code>com.google.android.wikinotes.db.WikiNotesProvider</code> is registered
+with the system as able to handle the
+<code>com.google.android.wikinotes.db.wikinotes</code> root authority (i.e. URIs
+starting with that identifier).</p>
+
+<p>From here, Android takes the rest of the URI and presents it to that
+ContentProvider. If you look at the
+<a href="http://code.google.com/p/apps-for-android/source/browse/trunk/WikiNotes/src/com/google/android/wikinotes/db/WikiNotesProvider.java">WikiNotesProvider
+class</a> and scroll to the very bottom, in the static block there, you can see
+the pattern definitions to match the rest of the URL.</p>
+
+<p>In particular, take a look at the two lines:</p>
+
+<pre>URI_MATCHER.addURI(WikiNote.WIKINOTES_AUTHORITY, "wikinotes", NOTES);
+URI_MATCHER.addURI(WikiNote.WIKINOTES_AUTHORITY, "wikinotes/*", NOTE_NAME);</pre>
+
+<p>These are the definitions of URIs that our ContentProvider recognizes and can
+handle. The first recognizes a full URI of
+<code>content://com.google.android.wikinotes.db.wikinotes/wikinotes</code> and
+associates that with a constant called NOTES. This is used elsewhere in the
+ContentProvider to provide a list of all of the wiki notes in the database when
+the URI is requested.</p>
+
+<p>The second line uses a wildcard — '*' — to match a request of the
+form that Linkify will create, e.g.
+<code>content://com.google.android.wikinotes.db.wikinotes/wikinotes/ToDoList
+</code>. In this example, the * matches the ToDoList part of the URI and is
+available to the handler of the request, so that it can fish out the matching
+note for ToDoList and return it as the data. This also associates that match
+with a constant called NOTE_NAME, which again is used as an identifier elsewhere
+in the ContentProvider.</p>
+
+<p>The other matches in this static block are related to forms of
+searching that have been implemented in the WikiNotes for Android
+application, and will be covered in later articles. Likewise, how the
+data is obtained from this matching pattern will be the subject of the
+next article.</p>
+
+<p>For right now we are concerned with the MIME type for the URI. This is
+defined in the <code>getType()</code> method also in the
+<a href="http://code.google.com/p/apps-for-android/source/browse/trunk/WikiNotes/src/com/google/android/wikinotes/db/WikiNotesProvider.java">WikiNotesProvider
+class</a> (about halfway through the file). Take a quick look at this. The key
+parts for now are:</p>
+
+<pre>case NOTES:
+ return "vnd.android.cursor.<b>dir</b>/vnd.google.wikinote";</pre>
+
+<p>and</p>
+
+<pre>case NOTE_NAME:
+ return "vnd.android.cursor.<b>item</b>/vnd.google.wikinote";</pre>
+
+<p>These are the same constant names we defined in our pattern
+matchers. In the first case, that of the all notes URI, the MIME type
+returned is <code>vnd.android.cursor.dir/vnd.google.wikinote</code>
+which is like saying an Android list (dir) of Google wiki notes (the
+vnd bit is MIME-speak for "vendor specific definition"). Likewise, in
+the case of a NOTE_NAME match, the MIME type returned is
+<code>vnd.android.cursor.item/vnd.google.wikinote</code> which is
+like saying an Android item of Google wiki notes.</p>
+
+<p>Note that if you define your own MIME data types like this, the
+<code>vnd.android.cursor.dir</code> and <code>vnd.android.cursor.item</code>
+categories should be retained, since they have meaning to the Android
+system, but the actual item types should be changed to reflect your
+particular data type.</p>
+
+<p>So far Android has been able to find a ContentProvider that handles
+the Content URI supplied by the Linkify Intent call, and has queried
+the ContentProvider to find out the MIME types for that URI. The final
+step is to find an activity that can handle the VIEW action for that
+MIME type. Take a look in the the
+<a href="http://code.google.com/p/apps-for-android/source/browse/trunk/WikiNotes/AndroidManifest.xml">AndroidManifest.xml file</a>
+again. Inside the WikiNotes activity definition, you will see:</p>
+
+<pre><intent-filter>
+ <action name="android.intent.action.VIEW"/>
+ <category name="android.intent.category.DEFAULT"/>
+ <category name="android.intent.category.BROWSABLE"/>
+ <data mimetype="vnd.android.cursor.item/vnd.google.wikinote"/>
+</intent-filter></pre>
+
+<p>This is the correct combination of matches for the VIEW action on a
+WikiNote type that is requested from the LINKIFY class. The DEFAULT
+category indicates that the WikiNotes activity should be treated as a
+default handler (a primary choice) for this kind of data, and the
+BROWSABLE category means it can be invoked from a "browser", in this
+case the marked-up Linkified text.</p>
+
+<p>Using this information, Android can match up the VIEW action request
+for the WikiNotes data type with the WikiNotes activity, and can then
+use the WikiNotes activity to handle the request.</p>
+
+<h3>Why do it like this?</h3>
+
+<p>It's quite a trip through the system, and there is a lot to absorb
+here, but this is one of the main reasons I wanted to write WikiNotes
+in the first place. If you follow and understand the steps here, you'll
+have a good grasp of the whole Intents mechanism in Android, and how it
+helps loosely coupled activities cooperate to get things done.</p>
+
+<p>In this case, we could have found another way to detect wiki words
+based on a regular expression, and maybe written our own handler to
+intercept clicks within the TextView and dig out the right data and
+display it. This would seem to accomplish the same functionality just
+as easily as using intents, so what is the advantage to using the full
+Intents mechanism?</p>
+
+<p>In fact there are several advantages:</p>
+
+<p>The most obvious is that because we are using the standard Intent
+based approach, we are not limited to just linking and navigating to
+other wiki notes. We get similar behavior to a number of other data
+types as well. For example, a telephone number or web URL in a wiki
+note will be marked up by Linkify, and using this same mechanism (VIEW
+action on the linked data type) the browser or dialer activities will
+be automatically fired.</p>
+
+<p>It also means that each operation on a wiki note can be treated as a
+separate life cycle by our activity. We are not dealing with swapping
+data in and out of an existing activity - each activity works on a
+particular wiki note and that's all you have to worry about.</p>
+
+<p>Another advantage is that we now have a public activity to handle
+VIEW actions in WikiNotes no matter where the request comes from.
+Another application could request to view a wiki note (perhaps without
+even knowing what kind of data it is) and our activity could start up
+and handle it.</p>
+
+<p>The backstack is automatically maintained for you too. As you
+forward navigate through WikiNotes, Android maintains the history of
+notes visited, and so when you hit the back button you go back to the
+last note you were on. All this is free because we rely on the Android
+intents mechanism.</p>
+
+<p>Finally, if you run WikiNotes for Android and then start DDMS to
+take a look at the Activity threads in the WikiNotes application while
+it is running, you can see that despite what you might think, letting
+Android manage the navigation is very efficient. Create a few linked
+notes, as many links deep as you like, and then follow them. If you
+follow links hundreds of notes deep, you will still only see a handful
+of WikiNotes activities. Android is managing the activities, closing
+the older ones as necessary and using the life cycle to swap data in
+and out.</p>
+
+<h3>Next Time</h3>
+
+<p>This was a long article, but necessarily so. It demonstrates the
+importance of the Intents mechanism and to reinforce the notion that it
+should be used whenever possible for forward navigation, even within a
+single application. Illustrating this is one of the primary reasons I
+wrote WikiNotes for Android in the first place.</p>
+
+<p>In the next article we will look deeper into the ContentProvider and
+examine how it turns a Content URI into a row (or several rows) of data
+that can be used by an activity.</p>
diff --git a/docs/html/resources/articles/wikinotes-linkify.jd b/docs/html/resources/articles/wikinotes-linkify.jd
new file mode 100644
index 0000000..21b1f13
--- /dev/null
+++ b/docs/html/resources/articles/wikinotes-linkify.jd
@@ -0,0 +1,113 @@
+page.title=WikiNotes: Linkify your Text!
+@jd:body
+
+<img style="margin-left: 1.5em; margin-bottom:1.5em; float: right;"
+src="images/WikiNotes.png" alt="Linkify example" border="0">
+
+<p>This article introduces <a
+href="http://android-developers.blogspot.com/2008/03/announcing-apps-for-android
+.html">WikiNotes for Android</a>, part of the <a
+href="http://code.google.com/p/apps-for-android/">Apps for Android</a>
+project. It covers the use of Linkify to turn ordinary text views
+into richer, link-oriented content that causes Android intents to fire
+when a link is selected.</p>
+
+<p><strong>Linkify</strong>: The {@link android.text.util.Linkify} class in the
+framework is perfect for creating a wiki note pad. It lets you specify a <a
+title="regular expression"
+href="http://en.wikipedia.org/wiki/Regular_expression">regular expression
+»</a>
+to match, and a scheme to prepend. The scheme is a string that, when
+the matched text is added, forms a Content URI to allow the correct
+data to be looked up.</p>
+
+<p>For example, in our case we want to look for a regular expression match for a
+WikiWord (that is, a word with <a title="word with camel case"
+href="http://en.wikipedia.org/wiki/CamelCase">camel case »</a> and no
+spaces). Linkify can then turn this into a Content URI — something like
+<code>content://com.google.android.wikinotes.db.wikinotes/wikinotes/WikiWord</code>,
+which can then be used to locate the correct wiki page from a
+{@link android.content.ContentProvider}.</p>
+
+<p>As a bonus, the Linkify class also defines several default matches,
+in particular it is able to turn web URLs, email addresses and
+telephone numbers into active links which fire Android intents
+automatically.</p>
+
+<p>Linkify can be passed any TextView in your application, and will
+take care of creating the links and enabling their "clickability" for
+you.</p>
+
+<p><strong>Default Linkify</strong>: Using the set of default active
+link options is very straightforward. Simply pass it a handle to a
+TextView with content in it, and the <code>Linkify.ALL</code> flag:</p>
+
+<pre>TextView noteView = (TextView) findViewById(R.id.noteview);
+noteView.setText(someContent);
+Linkify.addLinks(noteView, Linkify.ALL);</pre>
+
+<p>and that's it. The <code>Linkify.ALL</code> flag applies all of the predefined
+link actions, and the TextView will be immediately updated with a set
+of active links which, if you select them, fire default intents for the
+actions (e.g. a web URL will start the browser with that URL, a
+telephone number will bring up the phone dialer with that number ready
+to call, etc.).</p>
+
+<p><strong>Custom Linkify</strong>: So what about our WikiWord? There is no
+pre-defined action for that, so it needs to be defined and associated with a
+scheme.</p>
+
+<p>The first task is to define a regular expression that matches the kind of
+WikiWords we want to find. The regex in this case is:</p>
+
+<pre>\b[A-Z]+[a-z0-9]+[A-Z][A-Za-z0-9]+\b</pre>
+
+<p>Obvious, no? Well actually this is equivalent to the following
+description: "Starting with a word boundary (the \b) find at least one
+upper case letter, followed by at least one lower case letter or a
+numeric digit, followed by another upper case letter, and then any mix
+of upper case, lower case or numeric until the next word boundary (the
+final \b)". Regular expressions are not very pretty, but they are an
+extremely concise and accurate way of specifying a search pattern.</p>
+
+<p>We also need to tell Linkify what to do with a match to the
+WikiWord. Linkify will automatically append whatever is matched to a
+scheme that is supplied to it, so for the sake of argument let's assume
+we have a {@link android.content.ContentProvider} that matches the
+following content URI:</p>
+
+<pre>content://com.google.android.wikinotes.db.wikinotes/wikinotes/WikiWord</pre>
+
+<p>The WikiWord part will be appended by Linkify when it finds a match, so we
+just need the part before that as our scheme.</p>
+
+<p>Now that we have these two things, we use Linkify to connect them up:</p>
+
+<pre>Pattern wikiWordMatcher = Pattern.compile("\\b[A-Z]+[a-z0-9]+[A-Z][A-Za-z0-9]+\\b");
+String wikiViewURL = "content://com.google.android.wikinotes.db.wikinotes/wikinotes/";
+Linkify.addLinks(noteView, wikiWordMatcher, wikiViewURL);</pre>
+
+<p>Note that the \b's had to be escaped with double backslashes for the Java
+Pattern.compile line.</p>
+
+<p>Linkify can be used multiple times on the same view to add more
+links, so using this after the Default Linkify call means that the
+existing active links will be maintained and the new WikiWords will be
+added. You could define more Linkify actions and keep applying them to
+the same TextView if you wanted to.</p>
+
+<p>Now, if we have a WikiWord in the TextView, let's say
+<code>MyToDoList</code>, Linkify will turn it into an active link with the
+content URI:</p>
+
+<pre>content://com.google.android.wikinotes.db.wikinotes/wikinotes/MyToDoList</pre>
+
+<p>and if you click on it, Android will fire the default intent for that content
+URI.</p>
+
+<p>For this to all work, you will need a ContentProvider that
+understands that Content URI, and you will need a default activity
+capable of doing something with the resulting data. I plan to cover
+these in future blog entries (and soon). In fact, the whole Wiki Note
+Pad application is currently undergoing some clean up and review, and
+will then hopefully be released as a sample application.</p>
diff --git a/docs/html/resources/articles/window-bg-speed.jd b/docs/html/resources/articles/window-bg-speed.jd
new file mode 100644
index 0000000..bd7a303
--- /dev/null
+++ b/docs/html/resources/articles/window-bg-speed.jd
@@ -0,0 +1,125 @@
+page.title=Window Backgrounds & UI Speed
+@jd:body
+
+<p>Some Android applications require to squeeze every bit of performance out of
+the UI toolkit and there are many ways to do so. In this article, you will
+discover how to speed up the drawing and the <em>perceived</em> startup time of
+your activities. Both these techniques rely on a single feature, the window's
+background drawable.</p>
+
+<p>The term <em>window background</em> is a bit misleading, however. When you
+setup your user interface by calling <code>setContentView()</code> on an
+{@link android.app.Activity}, Android adds your views to the <code>Activity</code>'s
+window. The window however does not contain only your views, but a few others
+created for you. The most important one is, in the current implementation used
+on the T-Mobile G1, the <code>DecorView</code>, highlighted in the view
+hierarchy below:</p>
+
+<div style="text-align: center;"><img src="images/window_background_root.png" alt="A typical Android view hierarchy"></div>
+
+<p>The <code>DecorView</code> is the view that actually holds the
+window's background drawable. Calling
+{@link android.view.Window#setBackgroundDrawable(android.graphics.drawable.Drawable) getWindow().setBackgroundDrawable()}
+from your <code>Activity</code> changes the background of the window by changing
+the <code>DecorView</code>'s background drawable. As mentioned before, this
+setup is very specific to the current implementation of Android and can change
+in a future version or even on another device.</p>
+
+<p>If you are using the standard Android themes, a default background drawable
+is set on your activities. The standard theme currently used on the T-Mobile G1
+uses for instance a {@link android.graphics.drawable.ColorDrawable}. For most
+applications, this background drawable works just fine and can be left alone. It
+can however impacts your application's drawing performance. Let's take the
+example of an application that always draws a full screen opaque picture:</p>
+
+<div style="text-align: center;"><img src="images/window_background.png" alt="An opaque user interface doesn't need a window background"></div>
+
+<p>You can see on this screenshot that the window's background is invisible,
+entirely covered by an <code>ImageView</code>. This application is setup to
+redraw as fast as it can and draws at about 44 frames per second, or 22
+milliseconds per frame (<strong>note:</strong> the number of frames per second
+used in this article were obtained on a T-Mobile G1 with my finger on the screen
+so as to reduce the drawing speed which would otherwise be capped at 60 fps.) An
+easy way to make such an application draw faster is to <em>remove</em> the
+background drawable. Since the user interface is entirely opaque, drawing the
+background is simply wasteful. Removing the background improves the performance
+quite nicely:</p>
+
+<div style="text-align: center;"><img src="images/window_background_null.png" alt="Remove the background for faster drawing"></div>
+
+<p>In this new version of the application, the drawing speed went up to 51
+frames per second, or 19 milliseconds per frame. The difference of 3
+milliseconds per is easily explained by the speed of the memory bus on the
+T-Mobile G1: it is exactly the time it takes to move the equivalent of a
+screenful of pixels on the bus. The difference could be even greater if the
+default background was using a more expensive drawable.</p>
+
+<p>Removing the window's background can be achieved very easily by using
+a custom theme. To do so, first create a file called
+<code>res/values/theme.xml</code> containing the following:</p>
+
+<pre class="prettyprint"><resources>
+ <style name="Theme.NoBackground" parent="android:Theme">
+ <item name="android:windowBackground">@null</item>
+ </style>
+</resources></pre>
+
+<p>You then need to apply the theme to your activity by adding the attribute
+<code>android:theme="@style/Theme.NoBackground"</code> to your
+<code><activity /></code> or <code><application /></code> tag. This
+trick comes in very handy for any app that uses a <code>MapView</code>, a
+<code>WebView</code> or any other full screen opaque view.</p>
+
+<p><strong>Opaque views and Android</strong>: this optimization is currently
+necessary because the Android UI toolkit is not smart enough to prevent the
+drawing of views hidden by opaque children. The main reason why this
+optimization was not implemented is simply because there are usually very few
+opaque views in Android applications. This is however something that I
+definitely plan on implementing as soon as possible and I can only apologize for
+not having been able to do this earlier.</p><p>Using a theme to change the
+window's background is also a fantastic way to improve the <em>perceived</em>
+startup performance of some of your activities. This particular trick can only
+be applied to activities that use a custom background, like a texture or a logo.
+The <a href="http://code.google.com/p/shelves">Shelves</a> application is a good
+example:</p>
+
+<div style="text-align: center;"><img src="images/shelves2.png" alt="Textured backgrounds are good candidates for window's background"></div>
+
+<p>If this application simply set the wooden background in the XML layout or in
+<code>onCreate()</code> the user would see the application startup with the
+default theme and its dark background. The wooden texture would only appear
+after the inflation of the content view and the first layout/drawing pass. This
+causes a jarring effect and gives the user the impression that the application
+takes time to load (which can actually be the case.) Instead, the application
+defines the wooden background in a theme, picked up by the system as soon as the
+application starts. The user never sees the default theme and gets the
+impression that the application is up and running right away. To limit the
+memory and disk usage, the background is a tiled texture defined in
+<code>res/drawable/background_shelf.xml</code>:</p>
+
+<pre class="prettyprint"><bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/shelf_panel"
+ android:tileMode="repeat" /></pre><p>This drawable is simply referenced by the theme:</p>
+
+<pre class="prettyprint"><resources>
+ <style name="Theme.Shelves" parent="android:Theme">
+ <item name="android:windowBackground">@drawable/background_shelf</item>
+ <item name="android:windowNoTitle">true</item>
+ </style>
+</resources></pre>
+
+<p>The same exact trick is used in the <em>Google Maps application that ships
+with the T-Mobile G1. When the application is launched, the user immediately
+sees the loading tiles of <code>MapView</code>. This is only a trick, the theme
+is simply using a tiled background that looks exactly like the loading tiles of
+<code>MapView</code>.</em></p>
+
+<p>Sometimes the best tricks are also the simplest, so the next time you create
+an activity with an opaque UI or a custom background, remember to change the
+window's background.</p>
+
+<p><a href="http://progx.org/users/Gfx/android/WindowBackground">Download the source code of the first example</a>.</p>
+
+<p><a href="http://code.google.com/p/shelves/">Download the source code of Shelves</a>.</p>
+
+
diff --git a/docs/html/resources/articles/zipalign.jd b/docs/html/resources/articles/zipalign.jd
new file mode 100644
index 0000000..013d0fe
--- /dev/null
+++ b/docs/html/resources/articles/zipalign.jd
@@ -0,0 +1,98 @@
+page.title=Zipalign: an Easy Optimization
+@jd:body
+
+<p>The Android SDK includes a tool called <a
+href="{@docRoot}guide/developing/tools/zipalign.html"><code>zipalign</code></a>
+that optimizes the way an application is packaged. Running zipalign against your
+application enables Android to interact it more efficiently at run time and thus
+has the potential to make it and the overall system run faster. We strongly
+encourage you to use <code>zipalign</code> on both new and already published
+applications and to make the optimized version available — even if your
+application targets a previous version of Android. This article describes how
+<code>zipalign</code> helps performance and how to use it to optimize your
+app.</p>
+
+<p>In Android, data files stored in each application's apk are accessed by
+multiple processes: the installer reads the manifest to handle the
+permissions associated with that application; the Home application
+reads resources to get the application's name and icon; the system
+server reads resources for a variety of reasons (e.g. to display that
+application's notifications); and last but not least, the resource
+files are obviously used by the application itself.</p>
+
+<p>The resource-handling code in Android can efficiently access resources when
+they're aligned on 4-byte boundaries by memory-mapping them. But for resources
+that are not aligned (that is, when <code>zipalign</code> hasn't been run on an
+apk), it has to fall back to explicitly reading them — which is slower and
+consumes additional memory.</p>
+
+<p>For an application developer, this fallback mechanism is very
+convenient. It provides a lot of flexibility by allowing for several
+different development methods, including those that don't include
+aligning resources as part of their normal flow.</p>
+
+<p>Unfortunately, for users the situation is reversed — reading resources
+from unaligned apks is slow and takes a lot of memory. In the best case, the
+only visible result is that both the Home application and the unaligned
+application launch slower than they otherwise should. In the worst case,
+installing several applications with unaligned resources increases memory
+pressure, thus causing the system to thrash around by having to constantly start
+and kill processes. The user ends up with a slow device with a poor battery
+life.</p>
+
+<p>Luckily, it's very easy for you to align the resources in your application:</p>
+
+<ul>
+<li>Using ADT:</li>
+<li style="list-style-type: none; list-style-image: none; list-style-position: outside;">
+<ul>
+<li>The ADT plugin for Eclipse (starting from version 0.9.3) will automatically
+align release application packages if the export wizard is used to create them.
+To use the wizard, right click the project and choose "Android Tools" >
+"Export Signed Application Package..." It can also be accessed from the first
+page of the <code>AndroidManifest.xml</code> editor.</li>
+</ul>
+</li>
+<li>Using Ant:</li><li style="list-style-type: none; list-style-image: none; list-style-position: outside;">
+
+<ul>
+<li>The <em>Ant</em> build script (starting from Android 1.6) can align
+application packages. Targets for older versions of the Android platform are not
+aligned by the <em>Ant</em> build script and need to be manually aligned.</li>
+<li>Starting from the Android 1.6 SDK, Ant aligns and signs packages automatically,
+when building in debug mode.</li>
+<li>In release mode, Ant aligns packages only if it has enough
+information to sign the packages, since aligning has to happen after signing. In
+order to be able to sign packages, and therefore to align them, <em>Ant</em>
+needs to know the location of the keystore and the name of the key in
+<code>build.properties</code>. The name of the properties are
+<code>key.store</code> and <code>key.alias</code> respectively. If those
+properties are present, the signing tool will prompt to enter the store/key
+passwords during the build, and the script will sign and then align the apk
+file. If the properties are missing, the release package will not be signed, and
+therefore will not get aligned either.</li>
+</ul>
+</li>
+<li>Manually:</li>
+<li style="list-style-type: none; list-style-image: none; list-style-position: outside;">
+<ul>
+<li>In order to manually align a package, <code>zipalign</code>
+is in the <code>tools/</code> folder of Android 1.6 and later SDKs. You can use
+it to align application packages targeting any version of Android. You should run
+it only after signing the apk file, using the following command:
+<br><code>zipalign -v 4 source.apk destination.apk</code></li>
+</ul>
+</li>
+<li>Verifying alignment:</li>
+<li style="list-style-type: none; list-style-image: none; list-style-position: outside;">
+<ul>
+<li>The following command verifies that a package is aligned:<br><code>zipalign -c -v 4 application.apk</code>
+</li>
+</ul>
+</li>
+</ul>
+
+<p>We encourage you manually run <code>zipalign</code>
+on your currently published applications and to make the newly aligned
+versions available to users. Also, don't forget to align any new
+applications going forward!</p>
diff --git a/docs/html/resources/community-groups.jd b/docs/html/resources/community-groups.jd
new file mode 100644
index 0000000..61fbcc8
--- /dev/null
+++ b/docs/html/resources/community-groups.jd
@@ -0,0 +1,121 @@
+community=true
+page.title=Android Developer Groups
+@jd:body
+
+<p>Welcome to the Android developers community! We're glad you're here and invite you to participate in these discussions. Before posting, please read the <a href="http://source.android.com/discuss/android-discussion-groups-charter">Groups Charter</a> that covers the community guidelines.</p>
+
+<p class="note"><strong>Note:</strong> If you are seeking discussion about Android source code (not application development),
+then please refer to the <a href="http://source.android.com/discuss">Open Source Project Mailing lists</a>.</p>
+
+<p style="margin-bottom:.5em"><strong>Contents</strong></p>
+<ol class="toc">
+ <li><a href="#BeforeYouPost">Before you post</a></li>
+ <li><a href="#ApplicationDeveloperLists">Application developer mailing lists</a></li>
+ <li><a href="#UsingEmail">Using email with the mailing lists</a></li>
+</ol>
+
+<h2 id="BeforeYouPost">Before you post</h2>
+<p>Before writing a post, please try the following:</p>
+
+<ol>
+<li><a href="{@docRoot}resources/faq/index.html">Read the FAQs</a> The most common questions about developing Android applications are addressed in this frequently updated list.</li>
+<li><strong>Type in keywords of your questions in the main Android site's search bar</strong> (such as the one above). This search encompasses all previous discussions, across all groups, as well as the full contents of the site, documentation, and blogs. Chances are good that somebody has run into the same issue before.</li>
+<li><b>Search the mailing list archives</b> to see whether your questions have already been discussed.
+ </li>
+</ol>
+
+<p>If you can't find your answer, then we encourage you to address the community.
+As you write your post, please do the following:
+<ol>
+<li><b>Read
+the <a href="http://sites.google.com/a/android.com/opensource/discuss/android-discussion-groups-charter">mailing list charter</a></b> that covers the community guidelines.
+</li>
+<li><b>Select the most appropriate mailing list for your question</b>. There are several different lists for
+developers, described below.</li>
+<li>
+ <b>Be very clear</b> about your question
+in the subject -- it helps everyone, both those trying to answer your
+question as well as those who may be looking for information in the
+future.</li>
+<li><b>Give plenty of details</b> in your post to
+help others understand your problem. Code or log snippets, as well as
+pointers to screenshots, may also be helpful. For a great guide to
+phrasing your questions, read <a href="http://www.catb.org/%7Eesr/faqs/smart-questions.html">How To Ask Questions The Smart Way</a>.
+ </li>
+</ol>
+
+
+<h2 id="ApplicationDeveloperLists">Application developer mailing lists</h2>
+<ul>
+<li><b>Android beginners</b> - You're new to Android application development. You want to figure out how to get started with the Android SDK and the basic Android APIs? Start here. This list is open to any discussion around beginner-type questions for developers using the SDK; this is a great way to get up and running with your new application on the Android platform. Ask about getting your development environment set up, get help with the first steps of Android development (your first User Interface, your first permission, your first file on the Android filesystem, your first app on the Android Market...). Be sure to check the archives first before asking new questions. Please avoid advanced subjects, which belong on android-developers, and user questions, which will get a better reception on android-discuss.
+<ul>
+<li>Subscribe using Google Groups: <a href="http://groups.google.com/group/android-beginners">android-beginners</a></li>
+<li>Subscribe via email: <a href="mailto:android-beginners-subscribe@googlegroups.com">android-beginners-subscribe@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android developers</b> - You're now an experienced Android application developer. You've grasped the basics of Android app development, you're comfortable using the SDK, now you want to move to advanced topics. Get help here with troubleshooting applications, advice on implementation, and strategies for improving your application's performance and user experience. This is the not the right place to discuss user issues (use android-discuss for that) or beginner questions with the Android SDK (use android-beginners for that).
+<ul>
+<li>Subscribe using Google Groups: <a href="http://groups.google.com/group/android-developers">android-developers</a></li>
+<li>Subscribe via email: <a href="mailto:android-developers-subscribe@googlegroups.com">android-developers-subscribe@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android discuss</b> - The "water cooler" of Android discussion. You can discuss just about anything Android-related here, ideas for the Android platform, announcements about your applications, discussions about Android devices, community resources... As long as your discussion is related to Android, it's on-topic here. However, if you have a discussion here that could belong on another list, you are probably not reaching all of your target audience here and may want to consider shifting to a more targeted list.
+<ul>
+<li>Subscribe using Google Groups: <a href="http://groups.google.com/group/android-discuss">android-discuss</a></li>
+<li>Subscribe via email: <a href="mailto:android-discuss-subscribe@googlegroups.com">android-discuss-subscribe@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android ndk</b> - A place for discussing the Android NDK and topics related to using native code in Android applications.
+<ul>
+<li>Subscribe using Google Groups: <a href="http://groups.google.com/group/android-ndk">android-ndk</a></li>
+<li>Subscribe via email: <a href="mailto:android-ndk-subscribe@googlegroups.com">android-ndk-subscribe@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android security discuss</b> - A place for open discussion on secure development, emerging security concerns, and best practices for and by android developers. Please don't disclose vulnerabilities directly on this list, you'd be putting all Android users at risk.
+<ul>
+<li>Subscribe using Google Groups: <a href="http://groups.google.com/group/android-security-discuss">android-security-discuss</a></li>
+<li>Subscribe via email: <a href="mailto:android-security-discuss@googlegroups.com">android-security-discuss@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android security announce</b> - A low-volume group for security-related announcements by the Android Security Team.
+<ul>
+<li>Subscribe using Google Groups: <a href="http://groups.google.com/group/android-security-announce">android-security-announce</a></li>
+<li>Subscribe via email: <a href="mailto:android-security-announce-subscribe@googlegroups.com">android-security-announce-subscribe@googlegroups.com</a></li>
+</ul>
+</li>
+
+<li><b>Android Market Help Forum</b> - A web-based discussion forum where you can ask questions or report issues relating to Android Market.
+<ul>
+<li>URL: <a href="http://www.google.com/support/forum/p/Android+Market?hl=en">http://www.google.com/support/forum/p/Android+Market?hl=en</a></li>
+</ul>
+</li>
+
+</ul>
+
+
+
+<h2 id="UsingEmail">Using email with the mailing lists</h2>
+<p>Instead of using the <a href="http://groups.google.com/">Google Groups</a> site, you can use your email client of choice to participate in the mailing lists.</p>
+<p>To subscribe to a group without using the Google Groups site, use the link under "subscribe via email" in the lists above.</p>
+<p>To set up how you receive mailing list postings by email:</p>
+
+<ol><li>Sign into the group via the Google Groups site. For example, for the android-framework group you would visit <a href="http://groups.google.com/group/android-framework">http://groups.google.com/group/android-framework</a>.</li>
+<li>Click "Edit
+my membership" on the right side.</li>
+<li>Under "How do
+you want to read this group?" select one of the email options. </li>
+</ol>
+
+
+
+
+
+
+
+
+</div>
diff --git a/docs/html/resources/community-more.jd b/docs/html/resources/community-more.jd
new file mode 100644
index 0000000..2be015a
--- /dev/null
+++ b/docs/html/resources/community-more.jd
@@ -0,0 +1,48 @@
+community=true
+page.title=IRC and Twitter
+@jd:body
+
+<p>In addition to the <a href="community-groups.html">Android developer groups</a>, you can also participate in the Android developer community through IRC, and you can also follow us on Twitter. </p>
+
+<h3 id="irc">IRC</h3>
+
+<p>Several IRC channels are available for discussions about developing Android applications.</p>
+
+<table>
+<tr>
+ <th>Channel</th><th>Host</th><th>Description</th>
+</tr>
+<tr>
+ <td>#android</td>
+ <td>irc.freenode.net</td>
+ <td>General discussion about Android (and Android development).</td>
+</tr>
+<tr>
+ <td>#android-dev</td>
+ <td>irc.freenode.net</td>
+ <td>Discussion focused on developing Android apps.</td>
+</tr>
+</table>
+
+<p>If you haven't used IRC before, check <a href="http://en.wikipedia.org/wiki/List_of_IRC_clients">http://en.wikipedia.org/wiki/List_of_IRC_clients »</a> for a helpful list of IRC clients. Alternatively, you could also use this <a href="http://java.freenode.net/index.php?channel=android-dev">web interface »</a>, which does not require any installation, to join discussions on the Android IRC channels. </p>
+
+<p>Here are some tips for using IRC:</h4>
+
+<ul>
+<li>Set your nickname before you join the channel.</li>
+<li>Registering your nickname prevents others from using your nickname or impersonating you later:
+<pre>/nick <yournickname>
+/msg nickserv register <password> <email></code></pre>
+<p>Afterwards, when you connect, you'll need to supply a password:</p>
+<pre>/connect irc.freenode.net
+/nick <yournickname>
+/msg nickserv identify <password>
+/join #android-dev</pre>
+</li>
+</ul>
+
+<h3 id="twitter">Twitter</h3>
+<p>You can follow us on Twitter at this account:</p>
+
+<p style="margin-left:2em;"><a href="http://twitter.com/androiddev">http://twitter.com/androiddev</a></p>
+
diff --git a/docs/html/resources/faq/commontasks.jd b/docs/html/resources/faq/commontasks.jd
new file mode 100644
index 0000000..fa487f7
--- /dev/null
+++ b/docs/html/resources/faq/commontasks.jd
@@ -0,0 +1,821 @@
+page.title=Common Tasks and How to Do Them in Android
+parent.title=FAQs, Tips, and How-to
+parent.link=index.html
+@jd:body
+
+<ul>
+ <li><a href="#neweclipseandroidproject">Creating an Android Application using
+ the Eclipse plugin</a></li>
+ <li><a href="#newandroidprojectnoeclipse">Creating an Android Application without
+ the Eclipse plugin</a></li>
+ <li><a href="#addexternallibrary">Adding an External Library (.jar) using Eclipse</a></li>
+ <li><a href="#implementcallbacks">Implementing Activity callbacks</a> (Android
+ calls your activity at various key moments in its life cycle. You must know
+ how to handle each of these to draw your screen, initialize class members,
+ and acquire data.)</li>
+ <li><a href="#opennewscreen">Opening a new screen</a></li>
+ <li><a href="#listening">Listening for button clicks </a></li>
+ <li><a href="#configurewindowproperties">Configuring general window properties </a></li>
+ <li><a href="#localhostalias">Referring to localhost from the emulated environment</a></li>
+ <li><a href="#appstate">Storing and retrieving state</a></li>
+ <li><a href="{@docRoot}guide/topics/data/data-storage.html#preferences">Storing and retrieving preferences</a></li>
+ <li><a href="#storingandretrieving">Storing and retrieving larger or more complex
+ persistent data</a> (files and data) </li>
+ <li><a href="#playback">Playing audio, video, still, or other media files</a></li>
+ <li><a href="#broadcastreceivers">Listening for and broadcasting global messages
+ and setting alarms</a></li>
+ <li><a href="#alerts">Displaying alerts </a></li>
+ <li><a href="#progressbar">Displaying a progress bar</a> </li>
+ <li><a href="#addmenuitems">Adding items to the screen menu</a> </li>
+ <li><a href="#webpage">Display a web page</a> </li>
+ <li><a href="#binding">Binding to data</a></li>
+ <li><a href="#handle">Getting a Handle to a Screen Element</a></li>
+ <li><a href="#captureimages">Capture images from the phone camera </a></li>
+ <li><a href="#threading">Handling expensive operations in the UI thread</a></li>
+ <li><a href="#selectingtext">Selecting, highlighting, or styling portions of
+ text</a></li>
+ <li><a href="#querymap">Utilizing attributes in a Map query</a></li>
+ <li><a href="#filelist">List of files for an Android application</a></li>
+ <li><a href="#logging">Print messages to a log file</a></li>
+</ul>
+<p>The ApiDemos sample application includes many, many examples of common
+tasks and UI features. See the code inside
+<code><sdk>samples/ApiDemos</code> and the other sample applications
+under the <code>samples/</code> folder in the SDK.</p>
+
+
+<h2 id="neweclipseandroidproject">Creating an Android Application using the Eclipse Plugin</h2>
+
+<p>Using the Android Eclipse plugin is the fastest and easiest way
+to start creating a new Android application. The plugin automatically generates
+the correct project structure for your application, and keeps the resources
+compiled for you automatically.</p>
+
+<p>It is still a good idea to know what is going on though. Take a look at <a
+href="{@docRoot}guide/topics/fundamentals.html">Application Fundamentals</a>
+to understand the basics of how an Android application works.</p>
+
+<p>You should also take a look at the ApiDemos application and the other sample
+applications included in the SDK, in the <code><sdk>/samples/</code>
+folder in the SDK.</p>
+
+<p>Finally, a great way to started with Android development in Eclipse is to
+follow both the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello,
+World</a> and <a
+href="{@docRoot}resources/tutorials/notepad/index.html">Notepad</a> code
+tutorials. In particular, the start of the Hello Android tutorial is an
+excellent introduction to creating a new Android application in Eclipse.</p>
+
+<h2 id="newandroidprojectnoeclipse">Creating an Android Application without the Eclipse Plugin</h2>
+
+<p>This topic describes the manual steps in creating an Android application.
+Before reading this, you should read <a
+href="{@docRoot}guide/topics/fundamentals.html">Application Fundamentals</a>
+to understand the basics of how an Android application works. You might also
+want to look at the sample code included with the Android SDK, in the
+<code><sdk>/samples/</code> directory. </p>
+
+<p>Here is a list of the basic steps in building an application.</p>
+<ol>
+ <li><strong>Create your required resource files</strong> This includes
+ the AndroidManifest.xml global description file, string files that your application
+ needs, and layout files describing your user interface. A full list of optional
+ and required files and syntax details for each is given in <a href="#filelist">File
+ List for an Android Application</a>. </li>
+ <li><strong>Design your user interface</strong> See <a
+ href="{@docRoot}guide/topics/ui/index.html">User Interface</a> for
+ details on elements of the Android screen. </li>
+ <li><strong>Implement your Activity </strong>(this page)<strong> </strong> You
+ will create one class/file for each screen in your application. Screens will
+ inherit from an {@link android.app android.app} class, typically {@link android.app.Activity
+ android.app.Activity} for basic screens, {@link android.app.ListActivity
+ android.app.ListActivity} for list screens, or {@link android.app.Dialog
+ android.app.Dialog} for dialog boxes. You will implement the required callbacks
+ that let you draw your screen, query data, and commit changes, and also perform
+ any required tasks such as opening additional screens or reading data from
+ the device. Common tasks, such as opening a new screen or reading data from
+ the device, are described below.
+ The list of files you'll need for your application are described in <a href="#filelist">List
+ of Files for an Android Application</a>. </li>
+ <li><strong><a href="{@docRoot}guide/developing/other-ide.html#buildingwithant">Build and install your
+ package</a>.</strong> The Android SDK has some nice tools for generating
+ projects and debugging code. </li>
+</ol>
+
+<h2 id="addexternallibrary">Adding an External Library (.jar) using Eclipse</h2>
+<p>
+You can use a third party JAR in your application by adding it to your Eclipse project as follows:
+</p>
+<ol>
+<li>
+In the <strong>Package Explorer</strong> panel, right-click on your project and select <strong>Properties</strong>.
+<li>
+Select <strong>Java Build Path</strong>, then the tab <strong>Libraries</strong>.
+<li>
+Press the <strong>Add External JARs...</strong> button and select the JAR file.
+</ol>
+<p>
+Alternatively, if you want to include third party JARs with your package, create a new directory for them within your project and select <strong>Add Library...</strong> instead.</p>
+<p>
+It is not necessary to put external JARs in the assets folder.
+</p>
+
+<a name="implementcallbacks" id="implementcallbacks"></a>
+<h2>Implementing Activity Callbacks</h2>
+<p>Android calls a number of callbacks to let you draw your screen, store data before
+ pausing, and refresh data after closing. You must implement at least some of
+ these methods. See <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">Lifecycles</a>
+ discussion in Application Fundamentals to learn when and in what order these methods
+ are called. Here are some of the standard types of screen classes that Android provides:</p>
+<ul>
+ <li>{@link android.app.Activity android.app.Activity} - This is a standard screen,
+ with no specialization.</li>
+ <li>{@link android.app.ListActivity android.app.ListActivity} - This is a screen
+ that is used to display a list of something. It hosts a ListView object,
+ and exposes methods to let you identify the selected item, receive callbacks
+ when the selected item changes, and perform other list-related actions. </li>
+ <li>{@link android.app.Dialog android.app.Dialog} - This is a small, popup dialog-style
+ window that isn't intended to remain in the history stack. (It is not resizeable
+ or moveable by the user.)</li>
+</ul>
+
+<a name="opennewscreen" id="opennewscreen"></a><h2>Opening a New Screen</h2>
+<p>Your Activity will often need to open another Activity screen as it progresses.
+ This new screen can be part of the same application or part of another application,
+ the new screen can be floating or full screen, it can return a result, and you
+ can decide whether to close this screen and remove it from the history stack
+ when you are done with it, or to keep the screen open in history. These next
+ sections describe all these options. </p>
+<h3>Floating or full?<a name="floatingorfull" id="floatingorfull"></a></h3>
+<p>When you open a new screen you can decide whether to make it transparent or floating,
+ or full-screen. The choice of new screen affects the event sequence of events
+ in the old screen (if the new screen obscures the old screen, a different
+ series of events is called in the old screen). See <a
+ href="{@docRoot}guide/topics/fundamentals.html#lcycles">Lifecycles</a> discussion
+ in Application Fundamentals for details. </p>
+<p>Transparent or floating windows are implemented in three
+ standard ways: </p>
+<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>@android:style/Theme.Dialog</code>
+ in your AndroidManifest.xml file. For example:
+</ul>
+<blockquote>
+ <pre><activity class="AddRssItem" android:label="Add an item" android:theme="@android:style/Theme.Dialog"/>
+</pre>
+</blockquote>
+<p>Calling startActivity() or startActivityForResult() will open a new screen in whatever
+ way it defines itself (if it uses a floating theme it will be floating,
+ otherwise it will be full screen). </p>
+<h3>Opening a Screen </h3>
+<p>When you want to open a new screen, you can either explicitly specify the activity
+ class to open, or you can let the operating system decide which screen to open,
+ based upon the data and various parameters you pass in. A screen is opened by
+ calling {@link android.app.Activity#startActivity(android.content.Intent) startActivity}
+ and passing in an {@link android.content.Intent Intent} object, which specifies
+ the criteria for the handling screen. To specify a specific screen, call Intent.setClass
+ or setClassName with the exact activity class to open. Otherwise, set a variety
+ of values and data, and let Android decide which screen is appropriate to open.
+ Android will find one or zero Activities that match the specified requirements;
+ it will never open multiple activities for a single request. More information
+ on Intents and how Android resolves them to a specific class is given in the
+ {@link android.content.Intent Intent} topic. </p>
+<a name="intentexamples" id="intentexamples"></a><h3>Some Intent examples </h3>
+<p>The following snippet loads the com.android.samples.Animation1 class, and
+ passes it some arbitrary data.:</p>
+<pre>Intent myIntent = new Intent();
+myIntent.setClassName("com.android.samples", "com.android.samples.Animation1");
+myIntent.putExtra("com.android.samples.SpecialValue", "Hello, Joe!"); // key/value pair, where key needs current package prefix.
+startActivity(myIntent); </pre>
+<p>The next snippet requests that a Web page be opened by specifying the VIEW action,
+ and a URI data string starting with "http://" schema:</p>
+<pre>Intent myIntent = new Intent(Intent.VIEW_ACTION, Uri.parse("http://www.google.com"));</pre>
+<p>Here is the intent filter from the AndroidManifest.xml file for com.android.browser:</p>
+<pre><intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <scheme android:name="http" />
+ <scheme android:name="https" />
+ <scheme android:name="file" />
+</intent-filter> </pre>
+<p>Android defines a number of standard values, for instance the action constants
+ defined by {@link android.content.Intent}. You can define custom values, but
+ both the caller and handler must use them. See the <intent-filter>
+ tag description in <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml
+ File</a> for more information on the manifest syntax for the handling
+ application. </p>
+<a name="returningaresult" id="returningaresult"></a><h3>Returning a Result from a Screen</h3>
+<p>A window can return a result after it closes. This result will be passed back
+ into the calling Activity's {@link android.app.Activity#onActivityResult(int,int,android.content.Intent)
+ onActivityResult()} method, which can supply an Intent containing arbitrary data, along with
+ the request code passed to startActivityForResult(). Note that you must call the {@link
+ android.app.Activity#startActivityForResult(android.content.Intent,int) startActivityForResult()}
+ method that accepts a request code parameter to get this callback. The following
+ code demonstrates opening a new screen and retrieving a result. </p>
+<pre>// Open the new screen.
+public void onClick(View v){
+ // Start the activity whose result we want to retrieve. The
+ // result will come back with request code GET_CODE.
+ Intent intent = new Intent(this, com.example.app.ChooseYourBoxer.class);
+ startActivityForResult(intent, CHOOSE_FIGHTER);
+}
+
+// Listen for results.
+protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ // See which child activity is calling us back.
+ switch (resultCode) {
+ case CHOOSE_FIGHTER:
+ // This is the standard resultCode that is sent back if the
+ // activity crashed or didn't doesn't supply an explicit result.
+ if (resultCode == RESULT_CANCELED){
+ myMessageboxFunction("Fight cancelled");
+ }
+ else {
+ myFightFunction(data);
+ }
+ default:
+ break;
+ }
+}
+
+// Class SentResult
+// Temporary screen to let the user choose something.
+ private OnClickListener mLincolnListener = new OnClickListener(){
+ public void onClick(View v) {
+ Bundle stats = new Bundle();
+ stats.putString("height","6\'4\"");
+ stats.putString("weight", "190 lbs");
+ stats.putString("reach", "74\"");
+ setResult(RESULT_OK, "Lincoln", stats);
+ finish();
+ }
+ };
+
+ private OnClickListener mWashingtonListener = new OnClickListener() {
+ public void onClick(View v){
+ Bundle stats = new Bundle();
+ stats.putString("height","6\'2\"");
+ stats.putString("weight", "190 lbs");
+ stats.putString("reach", "73\"");
+ setResult(RESULT_OK, "Washington", Bundle);
+ finish();
+ }
+ };
+ </pre>
+<h3>Lifetime of the new screen </h3>
+<p>An activity can remove itself from the history stack by calling {@link android.app.Activity#finish()
+ Activity.finish()} on itself, or the activity that opened the screen can call
+ {@link android.app.Activity#finishActivity(int) Activity.finishActivity()}
+ on any screens that it opens to close them. </p>
+<a name="listening" id="listening"></a><h2>Listening for Button Clicks</h2>
+<p>Button click and other UI event capturing are covered in <a href="{@docRoot}guide/topics/ui/ui-events.html">Handling UI Events</a> on the UI Design page.</p>
+<a name="configurewindowproperties" id="configurewindowproperties"></a><h2>Configuring General Window Properties</h2>
+<p>You can set a number of general window properties, such as whether to display
+ a title, whether the window is floating, and whether it displays an icon, by
+ calling methods on the {@link android.view.Window Window} member
+ of the underlying View object for the window. Examples include calling {@link
+ android.app.Activity#getWindow() getWindow().requestFeature()} (or the convenience
+ method {@link android.app.Activity#requestWindowFeature(int) requestWindowFeature(<em>some_feature</em>)})
+ to hide the title. Here is an example of hiding the title bar:</p>
+<pre>//Hide the title bar
+requestWindowFeature(Window.FEATURE_NO_TITLE);
+</pre>
+<p>A better way to achieve the same end is to specify a theme in your Android
+Manifest file:</p>
+<pre><application android:icon="@drawable/icon" android:theme="@android:style/Theme.NoTitleBar">
+</pre>
+<p>This is preferable because it tells the system not to show a title bar while
+your application is starting up. With the explicit method call, your application
+will have a title bar visible to the user until <code>onCreate</code> runs.</p>
+<p>(Note that this can be applied to either the <code><application></code>
+tag or to individual <code><activity></code> tags.)</p>
+<a name="localhostalias" id="localhostalias"></a><h2>Referring to localhost from the emulated environment</h2>
+<p>
+If you need to refer to your host computer's <em>localhost</em>, such as when you
+want the emulator client to contact a server running on the same host, use the alias
+<code>10.0.2.2</code> to refer to the host computer's loopback interface.
+From the emulator's perspective, localhost (<code>127.0.0.1</code>) refers to its own
+loopback interface.
+</p>
+<a name="appstate" id="appstate"></a><h2>Storing and Retrieving State</h2>
+<p>If your application is dumped from memory because of space concerns, it will lose
+ all user interface state information such as checkbox state and text box values
+ as well as class member values. Android calls {@link android.app.Activity#onSaveInstanceState(android.os.Bundle)
+ Activity.onSaveInstanceState} before it pauses the application. This method hands in a {@link
+ android.os.Bundle Bundle} that can be used to store name/value pairs that will
+ persist and be handed back to the application even if it is dropped from memory.
+ Android will pass this Bundle back to you when it calls {@link android.app.Activity#onCreate(android.os.Bundle)
+ onCreate()}. This Bundle only exists while the application is still in the history
+ stack (whether or not it has been removed from memory) and will be lost when
+ the application is finalized. See the topics for {@link android.app.Activity#onSaveInstanceState} and
+ {@link android.app.Activity#onCreate} for
+ examples of storing and retrieving state.</p>
+<p>Read more about the lifecycle of an application in <a href="{@docRoot}guide/topics/fundamentals.html">Application Fundamentals</a>.</p>
+<h3>Storing and Retrieving Larger or More Complex Persistent Data<a name="storingandretrieving" id="storingandretrieving"></a></h3>
+<p>Your application can store files or complex collection objects, and reserve them
+ for private use by itself or other activities in the application, or it can expose
+ its data to all other applications on the device. See <a href="{@docRoot}guide/topics/data/data-storage.html">Storing,
+ Retrieving, and Exposing Data</a> to learn how to store and retrieve private data,
+ how to store and retrieve common data from the device, and how to expose your
+ private data to other applications.</p>
+<a name="playback" id="playback"></a><h2>Playing Media Files</h2>
+<p>Please see the document <a href="{@docRoot}guide/topics/media/index.html">Audio and Video</a> for more details.</p>
+<a name="broadcastreceivers" id="broadcastreceivers"></a><h2>Listening For and Broadcasting Global Messages, and Setting Alarms</h2>
+<p>You can create a listening class that can be notified or even instantiated whenever
+ a specific type of system message is sent.
+</p>
+<p>The listening classes, called broadcast receivers, extend {@link android.content.BroadcastReceiver
+ BroadcastReceiver}. If you want Android to instantiate the object whenever an appropriate
+ intent notification is sent, define the receiver with a <code><receiver></code> element
+ in the AndroidManifext.xml file. If the caller is expected to instantiate the
+ object in preparation to receive a message, this is not required. The receiver
+ will get a call to their {@link android.content.BroadcastReceiver#onReceive(android.content.Context,android.content.Intent)
+ BroadcastReceiver.onReceive()} method. A receiver can define an <code><intent-filter></code> tag
+ that describes the types of messages it will receive. Just as Android's IntentResolver
+ will look for appropriate Activity matches for a startActivity() call, it will
+ look for any matching Receivers (but it will send the message to all matching
+ receivers, not to the "best" match). </p>
+<p>To send a notification, the caller creates an {@link android.content.Intent Intent}
+ object and calls {@link android.app.Activity#sendBroadcast(android.content.Intent)
+ Context.sendBroadcast()} with that Intent. Multiple recipients can receive
+ the same message. You can broadcast an Intent message to an intent receiver in
+ any application, not only your own. If the receiving class is not registered
+ using <code><receiver></code> in its manifest, you can dynamically instantiate
+ and register a receiver by calling {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver,android.content.IntentFilter)
+ Context.registerReceiver()}. </p>
+<p>Receivers can include intent filters to specify what kinds of intents they are
+ listening for. Alternatively, if you expect a single known caller to contact
+ a single known receiver, the receiver does not specify an intent filter, and
+ the caller specifies the receiver's class name in the Intent by calling {@link
+ android.content.Intent#setClassName(java.lang.String, java.lang.String) Intent.setClassName()}
+ with the recipient's class name. The recipient receives a {@link android.content.Context
+ Context} object that refers to its own package, not to the package of the sender.</p>
+<p><em><strong>Note:</strong></em> If a receiver or broadcaster
+ enforces permissions, your application might need to request permission
+ to send or receive messages from that object. You can request permission by using
+ the <uses-permission> tag in the manifest. </p>
+<p>Here is a code snippet of a sender and receiver. This example does not demonstrate
+ registering receivers dynamically. For a full code example, see the AlarmService
+ class in the ApiDemos project.</p>
+<h3>Sending the message</h3>
+<pre>// We are sending this to a specific recipient, so we will
+// only specify the recipient class name.
+Intent intent = new Intent(this, AlarmReceiver.class);
+intent.putExtra("message","Wake up.");
+sendBroadcast(intent);
+</pre>
+<h3>Receiving the message</h3>
+<p><strong>Receiver AndroidManifest.xml </strong>(because there is no intent filter
+ child, this class will only receive a broadcast when the receiver class is specified
+ by name, as is done in this example):</p>
+<pre>
+<receiver class=".AlarmReceiver" /></pre>
+<p><strong>Receiver Java code: </strong></p>
+<pre>
+public class AlarmReceiver extends BroadcastReceiver{
+ // Display an alert that we've received a message.
+ @Override
+ public void onReceive(Context context, Intent intent){
+ // Send a text notification to the screen.
+ NotificationManager nm = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+ nm.notifyWithText(R.id.alarm,
+ "Alarm!!!",
+ NotificationManager.LENGTH_SHORT,
+ null);
+ }
+} </pre>
+<h3>Other system messages</h3>
+<p>You can listen for other system messages sent by Android as well, such as USB
+ connection/removal messages, SMS arrival messages, and timezone changes. See
+ {@link android.content.Intent} for a list of broadcast messages to listen for.
+ Messages are marked "Broadcast Action" in the documentation. </p>
+<h3>Listening for phone events<a name="phoneevents" id="phoneevents"></a></h3>
+<p>The {@link android.telephony android.telephony} package overview page describes how to
+ register to listen for phone events. </p>
+<a name="alarms" id="alarms"></a><h3>Setting Alarms </h3>
+<p>Android provides an {@link android.app.AlarmManager AlarmManager} service that
+ will let you specify an Intent to send at a designated time. This intent is typically
+ used to start an application at a preset time. (Note: If you want to send
+ a notification to a sleeping or running application, use {@link android.os.Handler
+ Handler} instead.)</p>
+<a name="alerts" id="alerts"></a><h2>Displaying Alerts</h2>
+<p>There are two major kinds of alerts that you may display to the user:
+(1) Normal alerts are displayed in response to a user action, such as
+trying to perform an action that is not allowed. (2) Out-of-band alerts,
+called notifications, are
+displayed as a result of something happening in the background, such as the
+user receiving new e-mail.</p>
+
+<a name="dialogsandalerts" id="dialogsandalerts"></a><h3>Normal Alerts</h3>
+
+<p>Android provides a number of ways for you to show popup notifications to your
+ user as they interact with your application. </p>
+<table width="100%" border="1">
+ <tr>
+ <th scope="col">Class</th>
+ <th scope="col">Description</th>
+ </tr>
+ <tr>
+ <td>{@link android.app.Dialog app.Dialog}</td>
+ <td>A generic floating dialog box with a layout that you design. </td>
+ </tr>
+ <tr>
+ <td><p>{@link android.app.AlertDialog app.AlertDialog}</p></td>
+ <td>A popup alert dialog with two buttons (typically OK and Cancel) that
+ take callback handlers. See the section after this table for more details. </td>
+ </tr>
+ <tr>
+ <td>{@link android.app.ProgressDialog ProgressDialog} </td>
+ <td>A dialog box used to indicate progress of an operation with a known progress
+ value or an indeterminate length (setProgress(bool)). See <strong>Views</strong> > <strong>Progress Bar</strong> in
+ ApiDemos for examples. </td>
+ </tr>
+ <tr>
+ <td>Activity</td>
+ <td>By setting the theme of an activity to
+ {@link android.R.style#Theme_Dialog
+ android:theme="@android:style/Theme.Dialog"},
+ your activity will take on
+ the appearance of a normal dialog, floating on top of whatever was
+ underneath it. You usually set the theme through the
+ {@link android.R.attr#theme android:theme} attribute in your AndroidManifest.xml.
+ The advantage of this
+ over Dialog and AlertDialog is that Application has a much better managed
+ life cycle than dialogs: if a dialog goes to the background and is killed,
+ you cannot recapture state, whereas Application exposes a {@link android.os.Bundle
+ Bundle} of saved values in <code>onCreate()</code> to help you maintain state.</td>
+ </tr>
+</table>
+<h3>AlertDialog</h3>
+<p>This is a basic warning dialog box that lets you configure a message, button text,
+ and callback. You can create one by calling using the {@link
+ android.app.AlertDialog.Builder} class, as shown here. </p>
+<pre>private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case ACCEPT_CALL:
+ answer(msg.obj);
+ break;
+
+ case BOUNCE_TO_VOICEMAIL:
+ voicemail(msg.obj);
+ break;
+
+ }
+ }
+};
+
+
+private void IncomingMotherInlawCall(Connection c) {
+ String Text;
+
+ // "Answer" callback.
+ Message acceptMsg = Message.obtain();
+ acceptMsg.target = mHandler;
+ acceptMsg.what = ACCEPT_CALL;
+ acceptMsg.obj = c.getCall();
+
+ // "Cancel" callback.
+ final Message rejectMsg = Message.obtain();
+ rejectMsg.target = mHandler;
+ rejectMsg.what = BOUNCE_TO_VOICEMAIL;
+ rejectMsg.obj = c.getCall();
+
+ new AlertDialog.Builder(this)
+ .setMessage("Phyllis is calling")
+ .setPositiveButton("Answer", acceptMsg)
+ .setOnCanceListener(new OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ rejectMsg.sendToTarget();
+ }});
+ .show();
+} </pre>
+
+<h3>Notifications</h3>
+
+<p>Out-of-band alerts should always be displayed using the
+{@link android.app.NotificationManager}, which allows you to tell the user
+about something they may be interested in without disrupting what they are
+currently doing. A notification can be anything from a brief pop-up box
+informing the user of the new information, through displaying a persistent
+icon in the status bar, to vibrating, playing sounds, or flashing lights to
+get the user's attention. In all cases, the user must explicitly shift their
+focus to the notification before they can interact with it.</p>
+
+<p>The following code demonstrates using NotificationManager to display a basic text
+ popup when a new SMS message arrives in a listening service, and provides the
+ current message count. You can see several more examples in the ApiDemos application,
+ under app/ (named <em>notification</em>*.java).</p>
+<pre>static void setNewMessageIndicator(Context context, int messageCount){
+ // Get the static global NotificationManager object.
+ NotificationManager nm = NotificationManager.getDefault();</p>
+
+ // If we're being called because a new message has been received,
+ // then display an icon and a count. Otherwise, delete the persistent
+ // message.
+ if (messageCount > 0) {
+ nm.notifyWithText(myApp.NOTIFICATION_GUID, // ID for this notification.
+ messageCount + " new message" + messageCount > 1 ? "s":"", // Text to display.
+ NotificationManager.LENGTH_SHORT); // Show it for a short time only.
+ }
+}</pre>
+<p>To display a notification in the status bar and have it launch an intent when
+ the user selects it (such as the new text message notification does), call {@link
+ android.app.NotificationManager#notify(int, android.app.Notification) NotificationManager.notify()},
+ and pass in vibration patterns, status bar icons, or Intents to associate with
+ the notification. </p>
+<a name="progressbar" id="progressbar"></a><h2>Displaying a Progress Bar</h2>
+<p>An activity can display a progress bar to notify the user that something is happening.
+ To display a progress bar in a screen, call {@link android.app.Activity#requestWindowFeature(int)
+ Activity.requestWindowFeature(Window.FEATURE_PROGRESS)}. To set the value
+ of the progress bar, call {@link android.view.Window#setFeatureInt(int,int)
+ Activity.getWindow().setFeatureInt(Window.FEATURE_PROGRESS, <em>level</em>)}.
+ Progress bar values are from 0 to 9,999, or set the value to 10,000 to make the
+ progress bar invisible. </p>
+<p>You can also use the {@link android.app.ProgressDialog ProgressDialog} class,
+ which enables a dialog box with an embedded progress bar to send a "I'm working
+ on it" notification to the user. </p>
+<a name="addmenuitems" id="addmenuitems"></a><h2>Adding Items to the Screen Menu</h2>
+<p>See <a href="{@docRoot}guide/topics/ui/menus.html">Creating Menus</a>.</p>
+
+<a name="webpage" id="webpage"></a><h2>Display a Web Page</h2>
+<p>Use the {@link android.webkit.WebView webkit.WebView} object. </p>
+<a name="binding" id="binding"></a><h2>Binding to Data</h2>
+<p>You can bind a ListView to a set of underlying data by using a shim class called
+ {@link android.widget.ListAdapter ListAdapter} (or a subclass). ListAdapter subclasses
+ bind to a variety of data sources, and expose a common set of methods such as
+ getItem() and getView(), and uses them to pick View items to display in its list.
+ You can extend ListAdapter and override getView() to create your own custom list
+ items. There are essentially only two steps you need to perform to bind to data: </p>
+<ol>
+ <li>Create a ListAdapter object and specify its data source</li>
+ <li>Give the ListAdapter to your ListView object.</li>
+</ol>
+<p>That's it!</p>
+<p>Here's an example of binding a ListActivity screen to the results from a cursor
+ query. (Note that the setListAdapter() method shown is a convenience method that
+ gets the page's ListView object and calls setAdapter() on it.)</p>
+<pre>// Run a query and get a Cursor pointing to the results.
+Cursor c = People.query(this.getContentResolver(), null);
+startManagingCursor(c);
+
+// Create the ListAdapter. A SimpleCursorAdapter lets you specify two interesting things:
+// an XML template for your list item, and
+// The column to map to a specific item, by ID, in your template.
+ListAdapter adapter = new SimpleCursorAdapter(this,
+ android.R.layout.simple_list_item_1, // Use a template that displays a text view
+ c, // Give the cursor to the list adapter
+ new String[] {People.NAME} , // Map the NAME column in the people database to...
+ new String[] {"text1"}); // The "text1" view defined in the XML template
+setListAdapter(adapter);</pre>
+<p>See view/List4 in the ApiDemos project for an example of extending ListAdapter
+ for a new data type. </p>
+
+<a name="handle"></a>
+
+<h2>Getting a Handle to a Screen Element</h2>
+<p>You can get a handle to a screen element by calling {@link
+android.app.Activity#findViewById(int) Activity.findViewById}. You can then use
+the handle to set or retrieve any values exposed by the object. </p>
+<a name="captureimages" id="captureimages"></a><h2>Capture Images from the Phone Camera</h2>
+<p>You can hook into the device's camera onto your own Canvas object by using the
+ {@link android.hardware.Camera Camera} class. See that class's documentation,
+ and the ApiDemos project's Camera Preview application (Graphics/Camera Preview)
+ for example code. </p>
+
+
+<a name="threading" id="threading"></a><h2>Handling Expensive Operations in the UI Thread</h2>
+<p>Avoid performing long-running operations (such as network I/O) directly in the UI thread —
+the main thread of an application where the UI is run — or your application may be blocked
+and become unresponsive. Here is a brief summary of the recommended approach for handling expensive operations:</p>
+<ol>
+<li>Create a Handler object in your UI thread</li>
+<li>Spawn off worker threads to perform any required expensive operations</li>
+<li>Post results from a worker thread back to the UI thread's handler either through a Runnable or a {@link android.os.Message}</li>
+<li>Update the views on the UI thread as needed</li>
+</ol>
+
+<p>The following outline illustrates a typical implementation:</p>
+
+<pre>
+public class MyActivity extends Activity {
+
+ [ . . . ]
+ // Need handler for callbacks to the UI thread
+ final Handler mHandler = new Handler();
+
+ // Create runnable for posting
+ final Runnable mUpdateResults = new Runnable() {
+ public void run() {
+ updateResultsInUi();
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ [ . . . ]
+ }
+
+ protected void startLongRunningOperation() {
+
+ // Fire off a thread to do some work that we shouldn't do directly in the UI thread
+ Thread t = new Thread() {
+ public void run() {
+ mResults = doSomethingExpensive();
+ mHandler.post(mUpdateResults);
+ }
+ };
+ t.start();
+ }
+
+ private void updateResultsInUi() {
+
+ // Back in the UI thread -- update our UI elements based on the data in mResults
+ [ . . . ]
+ }
+}
+</pre>
+
+<p>For further discussions on this topic, see
+<a href="{@docRoot}guide/practices/design/responsiveness.html">Designing for Responsiveness</a>
+and the {@link android.os.Handler} documentation.</p>
+
+<a name="selectingtext" id="selectingtext"></a><h2>Selecting, Highlighting, or Styling Portions of Text</h2>
+<p>You can highlight or style the formatting of strings or substrings of text in
+ a TextView object. There are two ways to do this:</p>
+<ul>
+ <li>If you use a <a href="{@docRoot}guide/topics/resources/available-resources.html#stringresources">string resource</a>,
+ you can add some simple styling, such as bold or italic using HTML notation.
+ The currently supported tags are: <code>B</code> (bold),
+ <code>I</code> (italic), <code>U</code> (underline),
+ <code>TT</code> (monospace), <code>BIG</code>, <code>SMALL</code>,
+ <code>SUP</code> (superscript), <code>SUB</code> (subscript),
+ and <code>STRIKE</code> (strikethrough).
+ So, for example, in res/values/strings.xml you could declare this:<br />
+ <code><resource><br />
+ <string>id="@+id/styled_welcome_message">We
+ are <b><i>so</i></b> glad to see you.</string><br />
+ </resources></code></li>
+ <li>To style text on the fly, or to add highlighting or more complex styling,
+ you must use the Spannable object as described next. </li>
+</ul>
+<p>To style text on the fly, you must make sure the TextView is using {@link android.text.Spannable}
+ storage for the text (this will always be true if the TextView is an EditText),
+ retrieve its text with {@link android.widget.TextView#getText}, and call {@link
+ android.text.Spannable#setSpan}, passing in a new style class from the {@link
+ android.text.style} package and the selection range. </p>
+<p>The following code snippet demonstrates creating a string with a highlighted section,
+ italic section, and bold section, and adding it to an EditText object. </p>
+<pre>// Get our EditText object.
+EditText vw = (EditText)findViewById(R.id.text);
+
+// Set the EditText's text.
+vw.setText("Italic, highlighted, bold.");
+
+// If this were just a TextView, we could do:
+// vw.setText("Italic, highlighted, bold.", TextView.BufferType.SPANNABLE);
+// to force it to use Spannable storage so styles can be attached.
+// Or we could specify that in the XML.
+
+// Get the EditText's internal text storage
+Spannable str = vw.getText();
+
+// Create our span sections, and assign a format to each.
+str.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+str.setSpan(new BackgroundColorSpan(0xFFFFFF00), 8, 19, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 21, str.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+</pre>
+
+<a name="querymap" id="querymap"></a><h2>Utilizing attributes in a Map query</h2>
+<p>
+When using a search intent to ask the Maps activity to search for something, the Maps activity responds to the following attributes in the optional context bundle:
+</p>
+<pre>
+ float "centerLatitude" default 0.0f
+ float "centerLongitude" default 0.0f
+ float "latitudeSpan" default 0.0f
+ float "longitudeSpan" default 0.0f
+ int "zoomLevel" default 10
+</pre>
+<p>
+This context information is used to center the search result in a particular area, and is equivalent to adjusting the Map activity to the described location and zoom level before issuing the query.
+</p>
+<p>
+If the latitudeSpan, longitudeSpan, and zoomLevel attributes are not consistent, then it is undefined which one takes precedence.
+</p>
+
+<a name="filelist" id="filelist"></a><h2>List of Files for an Android Application</h2>
+<p>The following list describes the structure and files of an Android application.
+ Many of these files can be built for you (or stubbed out) by the android tool
+ shipped in the tools/ menu of the SDK. </p>
+<table width="100%" border="0">
+ <tr>
+ <td width="28%" valign="top">MyApp/<br /></td>
+ <td width="72%" valign="top"> </td>
+ </tr>
+ <tr>
+ <td valign="top"> AndroidManifest.xml</td>
+ <td valign="top">(<em>required</em>) Advertises the screens that this application provides,
+ where they can be launched (from the main program menu or elsewhere),
+ any content providers it implements and what kind of data they handle,
+ where the implementation classes are, and other application-wide
+ information. Syntax details for this file are described in <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml File</a>.</td>
+ </tr>
+ <tr>
+ <td valign="top"> src/<br />
+ /<em>myPackagePath</em>/.../<em>MyClass</em>.java</td>
+ <td valign="top">(<em>required</em>) This folder holds all the source code files for your
+ application, inside the appropriate package subfolders. </td>
+ </tr>
+ <tr>
+ <td valign="top"> res/</td>
+ <td valign="top">(<em>required</em>) This folder holds all the <em>resources</em> for
+ your application. Resources are external data files or description files
+ that are compiled into your code at build time. Files in different folders
+ are compiled differently, so you must put the proper resource into the
+ proper folder. (See <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources</a> for details.)</td>
+ </tr>
+ <tr>
+ <td valign="top"> anim/<br />
+ <em>animation1</em>.xml<br />
+ <em>...</em></td>
+ <td valign="top">(<em>optional</em>) Holds any animation XML description files that the
+ application uses. The format of these files is described in <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources</a>. </td>
+ </tr>
+ <tr>
+ <td valign="top"> drawable/<br />
+ <em>some_picture</em>.png<br />
+ <em>some_stretchable</em>.9.png<br />
+ <em>some_background</em>.xml<br />
+ ...</td>
+ <td valign="top">(<em>optional</em>) Zero or more files that will be compiled to {@link
+ android.graphics.drawable android.graphics.drawable} resources. Files
+ can be image files (png, gif, or other) or XML files describing other
+ graphics such as bitmaps, stretchable bitmaps, or gradients. Supported
+ bitmap file formats are PNG (preferred), JPG, and GIF (discouraged),
+ as well as the custom 9-patch stretchable bitmap format. These formats
+ are described in <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources</a>. </td>
+ </tr>
+ <tr>
+ <td valign="top"> layout/<br />
+ <em>screen_1_layout</em>.xml<br />
+ ...<br /></td>
+ <td valign="top">(<em>optional</em>) Holds all the XML files describing screens or parts
+ of screens. Although you could create a screen in Java, defining them
+ in XML files is typically easier. A layout file is similar in concept
+ to an HTML file that describes the screen layout and components. See <a href="{@docRoot}guide/topics/ui/index.html">User Interface</a> for more information about designing screens, and <a href="{@docRoot}guide/topics/resources/available-resources.html#layoutresources">Available Resource Types</a> for the syntax of these files.</td>
+ </tr>
+ <tr>
+ <td valign="top"> values/<br />
+ arrays<br />
+ classes.xml<br />
+ colors.xml<br />
+ dimens.xml<br />
+ strings.xml<br />
+ styles.xml<br />
+ values.xml<br /></td>
+ <td valign="top"><p>(<em>optional</em>) XML files describing additional resources
+ such as strings, colors, and styles. The naming, quantity, and number
+ of these files are not enforced--any XML file is compiled, but these
+ are the standard names given to these files. However, the syntax
+ of these files is prescribed by Android, and described in <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources</a>. </p>
+ </td>
+ </tr>
+ <tr>
+ <td valign="top"> xml/</td>
+ <td valign="top">(<em>optional</em>) XML files that can be read at run time on the device. </td>
+ </tr>
+ <tr>
+ <td valign="top"> raw/</td>
+ <td valign="top">(<em>optional</em>) Any files to be copied directly to the device. </td>
+ </tr>
+</table>
+
+
+<a name="logging" ></a>
+<h2>Print Messages to a Log File</h2>
+
+<p>To write log messages from your application:</p>
+<ol><li>Import <code>android.util.Log</code>.</li>
+ <li>Use <code>Log.v()</code>, <code>Log.d()</code>, <code>Log.i()</code>,
+ <code>Log.w()</code>, or <code>Log.e()</code> to log messages.
+ (See the {@link android.util.Log} class.)<br/> E.g.,
+ <code>Log.e(this.toString(), "error: " + err.toString())</code></li>
+ <li>Launch <a href="{@docRoot}guide/developing/tools/ddms.html">DDMS</a> from a terminal
+ by executing <code>ddms</code> in your Android SDK <code>/tools</code> path.</li>
+ <li>Run your application in the Android emulator.</li>
+ <li>From the DDMS application, select the emulator
+ (e.g., "emulator-5554") and click <b>Device > Run logcat...</b>
+ to view all the log data.</li>
+</ol>
+<p class="note"><strong>Note:</strong> If you are running Eclipse and
+encounter a warning about the VM debug port when opening DDMS, you can ignore it
+if you're only interested in logs. However, if you want to further inspect and
+control your processes from DDMS, then you should close Eclipse before launching DDMS so that
+it may use the VM debugging port.</p>
+
+
diff --git a/docs/html/resources/faq/framework.jd b/docs/html/resources/faq/framework.jd
new file mode 100644
index 0000000..f4b8db0
--- /dev/null
+++ b/docs/html/resources/faq/framework.jd
@@ -0,0 +1,197 @@
+page.title=Android Application Framework FAQ
+parent.title=FAQs, Tips, and How-to
+parent.link=index.html
+@jd:body
+
+<ul>
+ <li><a href="#1">Do all the Activities and Services of an
+ application run in a single process?</a></li>
+ <li><a href="#2">Do all Activities run in the main thread of
+ an application process?</a></li>
+ <li><a href="#3">How do I pass complicated data structures
+ from one Activity/Service to another?</a></li>
+ <li><a href="#4">How can I check if an Activity is already
+ running before starting it?</a></li>
+ <li><a href="#5">If an Activity starts a remote service, is
+ there any way for the Service to pass a message back to the Activity?</a></li>
+ <li><a href="#6">How to avoid getting the Application not
+ responding dialog?</a></li>
+ <li><a href="#7">How does an application know if a package is
+ added or removed?</a></li>
+</ul>
+
+
+<a name="1" id="1"></a>
+
+<h2>Do all the Activities and Services of an application run in a
+single process?</h2>
+
+<p>All Activities and Services in an application run in a single process by
+default. If needed, you can declare an <code>android:process</code> attribute
+in your manifest file, to explicitly place a component (Activity/Service) in
+another process.</p>
+
+
+
+<a name="2" id="2"></a>
+
+<h2>Do all Activities run in the main thread of an application
+process?</h2>
+
+<p>By default, all of the application code in a single process runs
+in the main UI thread. This is the same thread
+that also handles UI events. The only exception is the code that handles
+IPC calls coming in from other processes. The system maintains a
+separate pool of transaction threads in each process to dispatch all
+incoming IPC calls. The developer should create separate threads for any
+long-running code, to avoid blocking the main UI thread.</p>
+
+
+
+<a name="3" id="3"></a>
+
+<h2>How do I pass data between Activities/Services within a single
+application?</h2>
+
+<p>It depends on the type of data that you want to share:</p>
+
+<h3>Primitive Data Types</h3>
+
+<p>To share primitive data between Activities/Services in an
+application, use Intent.putExtras(). For passing primitive data that
+needs to persist use the
+<a href="{@docRoot}guide/topics/data/data-storage.html#preferences">
+Preferences</a> storage mechanism.</p>
+
+<h3>Non-Persistent Objects</h3>
+
+<p>For sharing complex non-persistent user-defined objects for short
+duration, the following approaches are recommended:
+</p>
+ <h4>The android.app.Application class</h4>
+ <p>The android.app.Application is a base class for those who need to
+maintain global application state. It can be accessed via
+getApplication() from any Activity or Service. It has a couple of
+life-cycle methods and will be instantiated by Android automatically if
+your register it in AndroidManifest.xml.</p>
+
+ <h4>A public static field/method</h4>
+ <p>An alternate way to make data accessible across Activities/Services is to use <em>public static</em>
+fields and/or methods. You can access these static fields from any other
+class in your application. To share an object, the activity which creates your object sets a
+static field to point to this object and any other activity that wants to use
+this object just accesses this static field.</p>
+
+ <h4>A HashMap of WeakReferences to Objects</h4>
+ <p>You can also use a HashMap of WeakReferences to Objects with Long
+keys. When an activity wants to pass an object to another activity, it
+simply puts the object in the map and sends the key (which is a unique
+Long based on a counter or time stamp) to the recipient activity via
+intent extras. The recipient activity retrieves the object using this
+key.</p>
+
+ <h4>A Singleton class</h4>
+ <p>There are advantages to using a static Singleton, such as you can
+refer to them without casting getApplication() to an
+application-specific class, or going to the trouble of hanging an
+interface on all your Application subclasses so that your various
+modules can refer to that interface instead. </p>
+<p>But, the life cycle of a static is not well under your control; so
+to abide by the life-cycle model, the application class should initiate and
+tear down these static objects in the onCreate() and onTerminate() methods
+of the Application Class</p>
+</p>
+
+<h3>Persistent Objects</h3>
+
+<p>Even while an application appears to continue running, the system
+may choose to kill its process and restart it later. If you have data
+that you need to persist from one activity invocation to the next, you
+need to represent that data as state that gets saved by an activity when
+it is informed that it might go away.</p>
+
+<p>For sharing complex persistent user-defined objects, the
+following approaches are recommended:
+<ul>
+ <li>Application Preferences</li>
+ <li>Files</li>
+ <li>contentProviders</li>
+ <li>SQLite DB</li>
+</ul>
+</p>
+
+<p>If the shared data needs to be retained across points where the application
+process can be killed, then place that data in persistent storage like
+Application Preferences, SQLite DB, Files or ContentProviders. Please refer to
+the <a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a>
+for further details on how to use these components.</p>
+
+
+
+
+<a name="4" id="4"></a>
+
+<h2>How can I check if an Activity is already running before starting
+it?</h2>
+
+<p>The general mechanism to start a new activity if its not running—
+or to bring the activity stack to the front if is already running in the
+background— is the to use the NEW_TASK_LAUNCH flag in the startActivity()
+call.</p>
+
+
+
+<a name="5" id="5"></a>
+
+<h2>If an Activity starts a remote service, is there any way for the
+Service to pass a message back to the Activity?</h2>
+
+<p>The remote service can define a callback interface and register it with the
+clients to callback into the clients. The
+{@link android.os.RemoteCallbackList RemoteCallbackList} class provides methods to
+register and unregister clients with the service, and send and receive
+messages.</p>
+
+<p>The sample code for remote service callbacks is given in <a
+href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">ApiDemos/RemoteService</a></p>
+
+
+
+<a name="6" id="6"></a>
+
+<h2>How to avoid getting the Application not responding dialog?</h2>
+
+<p>Please read the <a href="{@docRoot}guide/practices/design/responsiveness.html">Designing for Responsiveness</a>
+document.</p>
+
+
+
+
+<a name="7" id="7"></a>
+
+<h2>How does an application know if a package is added or removed?
+</h2>
+
+<p>Whenever a package is added, an intent with PACKAGE_ADDED action
+is broadcast by the system. Similarly when a package is removed, an
+intent with PACKAGE_REMOVED action is broadcast. To receive these
+intents, you should write something like this:
+<pre>
+ <receiver android:name ="com.android.samples.app.PackageReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_ADDED"/>
+ <action android:name="android.intent.action.PACKAGE_REMOVED"/>
+
+ <data android:scheme="package" />
+ </intent-filter>
+ </receiver>
+ </pre>
+ <br>
+Here PackageReceiver is a BroadcastReceiver class.Its onReceive()
+method is invoked, every time an application package is installed or
+removed.
+
+</p>
+
+
+
diff --git a/docs/html/resources/faq/index.jd b/docs/html/resources/faq/index.jd
new file mode 100644
index 0000000..9a2614b
--- /dev/null
+++ b/docs/html/resources/faq/index.jd
@@ -0,0 +1,11 @@
+page.title=Android FAQs
+@jd:body
+
+<dl>
+ <dt><a href="framework.html">Application Framework FAQ</a></dt>
+ <dd>Common questions about the Android Application Framework.</dd>
+ <dt><a href="licensingandoss.html">Open Source Licensing FAQ</a></dt>
+ <dd>Common topics around licensing and Android Open Source</dd>
+ <dt><a href="security.html">Android Security FAQ</a></dt>
+ <dd>Answers to common questions about Android security.</dd>
+</dl>
diff --git a/docs/html/resources/faq/licensingandoss.jd b/docs/html/resources/faq/licensingandoss.jd
new file mode 100644
index 0000000..c267fe81
--- /dev/null
+++ b/docs/html/resources/faq/licensingandoss.jd
@@ -0,0 +1,19 @@
+page.title=Android Open Source Licensing FAQ
+parent.title=FAQs, Tips, and How-to
+parent.link=index.html
+@jd:body
+
+<ul>
+ <li><a href="#mirror">Where can I find the open source components of Android?</a></li>
+ <li><a href="#timeline">When will we see more code released under open source licenses?</a></li>
+ <li><a href="#apache2">Why are you releasing the code under the Apache License instead of GPLv2?</a></li>
+</ul>
+
+<a name="mirror" id="mirror"></a><h2>Where can I find the open source components of Android?</h2>
+<p>The source code for the full Android stack is available from the <a href="http://source.android.com">Android Open Source Project </a> site.
+
+<p>Other mirrored GPL and LGPL'd components are available at <a href="http://code.google.com/p/android/downloads/list"><code>http://code.google.com/p/android/downloads/list</code></a>.</p>
+<p>Notices for other licenses can be found within the SDK.</p>
+
+<a name="apache2" id="apache2"></a><h2>Why are you releasing the code under the Apache License instead of GPLv2?</h2>
+<p>One of the best explanations for the reasoning behind releasing code under Apache2 can be found in a <a href="http://arstechnica.com/news.ars/post/20071106-why-google-chose-the-apache-software-license-over-gplv2.html">ArsTechnica article</a> by Ryan Paul.</p>
diff --git a/docs/html/resources/faq/security.jd b/docs/html/resources/faq/security.jd
new file mode 100644
index 0000000..b0d832b
--- /dev/null
+++ b/docs/html/resources/faq/security.jd
@@ -0,0 +1,156 @@
+page.title=Android Security FAQ
+parent.title=FAQs, Tips, and How-to
+parent.link=index.html
+@jd:body
+
+<ul>
+ <li><a href="#secure">Is Android Secure?</a></li>
+ <li><a href="#issue">I think I found a security flaw. How do I report
+ it?</a></li>
+ <li><a href="#informed">How can I stay informed of Android security
+ announcements?</a></li>
+ <li><a href="#use">How do I securely use my Android phone?</a></li>
+ <li><a href="#malware">I think I found malicious software being distributed
+ for Android. How can I help?</a></li>
+ <li><a href="#fixes">How will Android-powered devices receive security fixes?</a>
+ </li>
+ <li><a href="#directfix">Can I get a fix directly from the Android Platform
+ Project?</a></li>
+</ul>
+
+
+<a name="secure" id="secure"></a><h2>Is Android secure?</h2>
+
+<p>The security and privacy of our users' data is of primary importance to the
+Android Open Source Project. We are dedicated to building and maintaining one
+of the most secure mobile platforms available while still fulfilling our goal
+of opening the mobile device space to innovation and competition.</p>
+
+<p>The Android Platform provides a rich <a
+href="http://code.google.com/android/devel/security.html">security model</a>
+that allows developers to request the capabilities, or access, needed by their
+application and to define new capabilities that other applications can request.
+The Android user can choose to grant or deny an application's request for
+certain capabilities on the handset.</p>
+
+<p>We have made great efforts to secure the Android platform, but it is
+inevitable that security bugs will be found in any system of this complexity.
+Therefore, the Android team works hard to find new bugs internally and responds
+quickly and professionally to vulnerability reports from external researchers.
+</p>
+
+
+<a name="issue" id="issue"></a><h2>I think I found a security flaw. How do I
+report it?</h2>
+
+<p>You can reach the Android security team at <a
+href="mailto:security@android.com">security@android.com</a>. If you like, you
+can protect your message using our <a
+href="http://code.google.com/android/security_at_android_dot_com.txt">PGP
+key</a>.</p>
+
+<p>We appreciate researchers practicing responsible disclosure by emailing us
+with a detailed summary of the issue and keeping the issue confidential while
+users are at risk. In return, we will make sure to keep the researcher informed
+of our progress in issuing a fix and will properly credit the reporter(s) when
+we announce the patch. We will always move swiftly to mitigate or fix an
+externally-reported flaw and will publicly announce the fix once patches are
+available to users.</p>
+
+
+<a name="informed" id="informed"></a><h2>How can I stay informed of Android
+security announcements?</h2>
+
+<p>An important part of sustainably securing a platform, such as, Android is
+keeping the user and security community informed of bugs and fixes. We will
+publicly announce security bugs when the fixes are available via postings to
+the <a
+href="http://groups.google.com/group/android-security-announce">android-security-announce</a>
+group on Google Groups. You can subscribe to this group as you would a mailing
+list and view the archives here.</p>
+
+<p>For more general discussion of Android platform security, or how to use
+security features in your Android application, please subscribe to <a
+href="http://groups.google.com/group/android-security-discuss">android-security-discuss</a>.
+</p>
+
+
+<a name="use" id="use"></a><h2>How do I securely use my Android phone?</h2>
+
+<p>As an open platform, Android allows users to load software from any
+developer onto a device. As with a home PC, the user must be
+aware of who is providing the software they are downloading and must decide
+whether they want to grant the application the capabilities it requests.
+This decision can be informed by the user's judgment of the software
+developer's trustworthiness, and where the software came from.</p>
+
+<p>Despite the security protections in Android, it is important
+for users to only download and install software from developers they trust.
+More details on how Android users can make smart security decisions will be
+released when consumer devices become available.</p>
+
+
+<a name="malware" id="malware"></a><h2>I think I found malicious software being
+distributed for Android. How can I help?</h2>
+
+<p>Like any other open platform, it will be possible for unethical developers
+to create malicious software, known as <a
+href="http://en.wikipedia.org/wiki/Malware">malware</a>, for Android. If you
+think somebody is trying to spread malware, please let us know at <a
+href="mailto:security@android.com">security@android.com</a>. Please include as
+much detail about the application as possible, with the location it is
+being distributed from and why you suspect it of being malicious software.</p>
+
+<p>The term <i>malicious software</i> is subjective, and we cannot make an
+exhaustive definition. Some examples of what the Android Security Team believes
+to be malicious software is any application that:
+<ul>
+ <li>drains the device's battery very quickly;</li>
+ <li>shows the user unsolicited messages (especially messages urging the
+ user to buy something);</li>
+ <li>resists (or attempts to resist) the user's effort to uninstall it;</li>
+ <li>attempts to automatically spread itself to other devices;</li>
+ <li>hides its files and/or processes;</li>
+ <li>discloses the user's private information to a third party, without the
+ user's knowledge and consent;</li>
+ <li>destroys the user's data (or the device itself) without the user's
+ knowledge and consent;</li>
+ <li>impersonates the user (such as by sending email or buying things from a
+ web store) without the user's knowledge and consent; or</li>
+ <li>otherwise degrades the user's experience with the device.</li>
+</ul>
+</p>
+
+
+<a name="fixes" id="fixes"></a><h2>How will Android-powered devices receive security
+fixes?</h2>
+
+<p>The manufacturer of each device is responsible for distributing software
+upgrades for it, including security fixes. Many devices will update themselves
+automatically with software downloaded "over the air", while some devices
+require the user to upgrade them manually.</p>
+
+<p>When Android-powered devices are publicly available, this FAQ will provide links how
+Open Handset Alliance members release updates.</p>
+
+<a name="directfix" id="directfix"></a><h2>Can I get a fix directly from the
+Android Platform Project?</h2>
+
+<p>Android is a mobile platform that will be released as open source and
+available for free use by anybody. This means that there will be many
+Android-based products available to consumers, and most of them will be created
+without the knowledge or participation of the Android Open Source Project. Like
+the maintainers of other open source projects, we cannot build and release
+patches for the entire ecosystem of products using Android. Instead, we will
+work diligently to find and fix flaws as quickly as possible and to distribute
+those fixes to the manufacturers of the products.</p>
+
+<p>In addition, We will add security fixes to the open source distribution of
+Android and publicly announce the changes on <a
+href="http://groups.google.com/group/android-security-announce">android-security-announce</a>.
+</p>
+
+<p>If you are making an Android-powered device and would like to know how you can
+properly support your customers by keeping abreast of software updates, please
+contact us at <a
+href="mailto:info@openhandsetalliance.com">info@openhandsetalliance.com</a>.</p>
diff --git a/docs/html/resources/faq/troubleshooting.jd b/docs/html/resources/faq/troubleshooting.jd
new file mode 100644
index 0000000..0cf1ab0
--- /dev/null
+++ b/docs/html/resources/faq/troubleshooting.jd
@@ -0,0 +1,261 @@
+page.title=Troubleshooting
+parent.title=FAQs, Tips, and How-to
+parent.link=index.html
+@jd:body
+
+
+<p>Here are some tips and tricks for common Android errors. Don't forget to use the
+ ddms logcat capability to get a deeper view when errors occur. See <a href="{@docRoot}guide/developing/debug-tasks.html">Debugging</a> for more debugging tips. </p>
+<ul>
+ <li><a href="#installeclipsecomponents">ADT Installation Error: "requires plug-in org.eclipse.wst.sse.ui".</a></li>
+ <li><a href="#nodevice">ADB reports "no device" when an emulator is running</a></li>
+ <li><a href="#noapp">My new application/activity isn't showing up in the device application
+ list </a></li>
+ <li><a href="#noupdate">I updated my app, but the updates don't seem to be showing up on
+ the device</a></li>
+ <li><a href="#layout_wilih">I'm getting a "Binary XML file line #2: You must supply a layout_wilih
+ attribute" error when I start an application</a></li>
+ <li><a href="#permission">My request to (<em>make a call, catch an incoming SMS, receive
+ a notification, send an intent to an Android application</em>) is being
+ ignored</a></li>
+ <li><a href="#build">Help! My project won't build in Eclipse</a></li>
+ <li><a href="#eclipse">Eclipse isn't talking to the emulator</a></li>
+ <li><a href="#majorminor">When I go to preferences in Eclipse and select "Android", I get the following error message: Unsupported major.minor version 49.0.</a></li>
+ <li><a href="#apidemosreinstall">I can't install ApiDemos apps in my IDE because of a signing error</a></li>
+ <li><a href="#signingcalendar">I can't compile my app because the build tools generated an expired debug certificate</a></li>
+</ul>
+
+<a name="installeclipsecomponents" id="installeclipsecomponents"></a><h2>ADT Installation Error: "requires plug-in org.eclipse.wst.sse.ui".</h2>
+<p>
+The "Android Editors" feature of the ADT Plugin requires specific Eclipse components, such as WST. If you
+encounter this error message during ADT installation, you need to install the
+required Eclipse components and then try the ADT installation again. Follow the steps below to install the required components for the
+Android Editors feature, based on the version of Eclipse that you are using.</p>
+
+<table style="font-size:100%">
+<tr><th>Eclipse 3.3 (Europa)</th><th>Eclipse 3.4 (Ganymede)</th></tr>
+<tr>
+<td width="50%">
+<ol>
+<li>From the dialog where you select the <strong>Update sites to visit</strong>, select the checkboxes for both the
+ADT site, and the Callisto/Europa/Ganymede Discovery Site (you may want to
+check <strong>Automatically select mirrors</strong> at the bottom).</li>
+<li>Click <strong>Finish</strong>.</li>
+<li>In the <strong>Next</strong> dialog, select the Android Plugins.</li>
+<li>Now, expand the tree item of the discovery site. It seems that if you
+don't do it, it doesn't load the content of the discovery site.</li>
+<li>On the right, click <strong>Select required</strong>. This will select all the components
+that are required to install the Android plugin (wst, emf, etc...).</li>
+<li>Click <strong>Next</strong>, accept the agreement, click <strong>Install All</strong>, and restart Eclipse.</li>
+</ol>
+</td>
+<td>
+<ol>
+ <li>Select <strong>Help</strong> > <strong>Software Updates...</strong></li>
+ <li>Select the <strong>Installed Software</strong> tab.</li>
+ <li>Click <strong>Update...</strong></li>
+ <li>If an update for ADT is available, select it and click <strong>Finish</strong>.</li>
+</ol>
+</td>
+</tr>
+</table>
+
+
+</p>
+<a name="nodevice"></a><h2>ADB reports "no device" when an emulator is running</h2>
+ <p>Try restarting adb by stopping it (<code>adb
+ kill-server</code>) then any other adb command to restart it.</p>
+
+<a name="noapp"></a><h2>My new application/activity isn't showing up in the
+ applications list </h2>
+<ul>
+ <li>You often must restart your device or emulator before a new activity shows
+ up in the applications list. This is particularly true when it is a completely
+ new application with a new AndroidManifest.xml file.</li>
+ <li>If this is for a new activity in an existing AndroidManifest.xml file, did
+ you include an <code><activity></code> tag for your app (or a <code><service></code> tag
+ for a service, or a <code><receiver></code> tag for a receiver, etc.)? </li>
+ <li>Make sure that your AndroidManifest.xml file is valid. Errors in attribute
+ values, such as the <em>value </em> attribute in <code><action <em>value</em>="<em><something></em>"></code>
+ will often not be caught by compilers, but will prevent your application
+ from being displayed because the intent filter will not be matched. Extra
+ spaces or other characters can often sneak into these strings.</li>
+ <li>Did you send your .apk file to the device (<a href="{@docRoot}guide/developing/tools/adb.html#move">adb install</a>)?</li>
+ <li>Run logcat on your device (<code>adb logcat</code>)
+ and then install your .apk file. Check the logcat output to see whether the
+ application is being installed and recognized properly. Here's sample output
+ from a successful installation:
+<pre>I/FileObserver( 414): *** onEvent wfd: 3 mask: 8 path: MyRSSReader.apk
+D/PackageManager( 414): Scanning package: /data/app/MyRSSReader.apk
+D/PackageManager( 414): Adding package com.example.codelab.rssexample
+D/PackageManager( 414): Registered content provider: my_rss_item, className = com.example.codelab.rssexample.RssContentProvider, isSyncable = false
+D/PackageManager( 414): Providers: com.example.codelab.rssexample.RssContentProvider
+D/PackageManager( 414): Activities: com.example.codelab.rssexample.MyRssReader com.example.codelab.rssexample.MyRssReader2 </pre>
+ </li>
+ <li>If logcat shows that the package manager is having problems loading the manifest
+ file, force your manifest to be recompiled by adding a space in the file and
+ compiling it.</li>
+</ul>
+<a name="noupdate"></a><h2>I updated my app, but the updates don't seem to be showing up on the device</h2>
+ <p>Did you remember to send your .apk file to the device (<a href="{@docRoot}guide/developing/tools/adb.html#move">adb
+ install</a>)?</p>
+
+<a name="layout_wilih"></a><h2>I'm getting a "Binary XML file line #2: You must supply a layout_wilih
+ attribute" error
+ when I start an application (but I declare a layout_wilih attribute <em>right
+ there!!!</em>)</h2>
+<ul>
+ <li>Make sure that the SDK you are building with is the same version as the Android
+ OS that you are running on. </li>
+ <li>Make sure that you're calling setContentView() early in your onCreate() method.
+ Calling other methods, such as setListAdapter() before calling setContentView()
+ can sometimes create odd errors when Android tries to access screen elements
+ that haven't been set before.</li>
+</ul>
+<a name="permission"></a><h2>My request to (<em>make a call, catch an incoming SMS,
+receive a notification, send an intent to an Android application</em>) is being
+ignored</h2>
+ <p>You might not have permission (or might not have requested permission) to
+ call this activity or receive this intent. Many standard Android activities,
+ such as making a call, have a permission assigned to it to prevent arbitrary
+ applications from sending or receiving requests. See <a
+ href="{@docRoot}guide/topics/security/security.html">Security and
+ Permissions</a> for more information on permissions, and
+ {@link android.Manifest.permission Manifest.permission} for a list of
+ standard permissions supported by the Android platform.
+</p>
+<a name="build"></a><h2>Help! My project won't build in Eclipse</h2>
+<p>If your project doesn't build, you may notice symptoms such as new
+resources added in the <code>res/</code> sub-folders not showing up in the R class,
+the emulator not being started, not being able to run the application, or even seeming to run an old version of the application.</p>
+<p>To troubleshoot these types of problems, first try:</p>
+<ol>
+ <li>Switch to the DDMS view in Eclipse (if you don't already have it open):
+ <ol type="a">
+ <li>From the menu select <code>Window > Open Perspective > Other</code></li>
+ <li>Select DDMS from the list and hit OK</li>
+ </ol>
+ </li>
+ <li>In the Devices panel (top right panel by default), click on the down triangle
+ to bring up the panel menu</li>
+ <li>Select <code>Reset ADB</code> from the menu, and then try running the
+ application again</li>
+</ol>
+<p>If the above still doesn't work, you can try these steps:</p>
+<ol>
+ <li>
+ Check the console and problems tabs at the bottom of the Eclipse UI
+ </li>
+ <li>
+ If there are problems listed in either place, they should give you a clue
+ what is wrong
+ </li>
+ <li>
+ If you aren't sure if the problems are fresh or stale, clear the console
+ with a right click > Clear, then clean the project
+ </li>
+ <li>
+ To clean the project (a good idea with any kind of build error), select
+ Project > Clean from the eclipse main menu, then select the project you
+ are working on (or clean all)
+ </li>
+</ol>
+<a name="eclipse"></a><h2>Eclipse isn't talking to the emulator</h2>
+<p>When communication doesn't seem to be happening between Eclipse and the emulator, symptoms can include: nothing happening when you press run, the emulator hanging waiting
+for a debugger to connect, or errors that Eclipse reports about not being able
+to find the emulator or shell. By far the most common symptom is that when you press run, the emulator starts (or
+is already running), but the application doesn't start.</p>
+<p>
+You may find any of these steps will fix the problem and with practice you
+probably can figure out which one you need to do for your particular issue, but
+to start with, the safest option is to run through all of them in order:</p>
+<ol>
+ <li>
+ Quit the emulator if it is running
+ </li>
+ <li>
+ Check that any emulator processes are killed (sometimes they can hang, use ps on unix or mac, or task manager in the process view on
+ windows).
+ </li>
+ <li>
+ Quit Eclipse
+ </li>
+ <li>
+ From the command line, type:
+<pre>adb kill-server </pre>
+ </li>
+ <li>
+ Start Eclipse and try again
+ </li>
+</ol>
+
+<a name="majorminor"></a><h2>When I go to preferences in Eclipse and select "Android", I get the following error message: Unsupported major.minor version 49.0.</h2>
+<p>This error is displayed if you are using an older version of the JDK. Please make sure you are using JDK version 5 or 6.</p>
+
+<h2 id="apidemosreinstall">I can't install ApiDemos apps in my IDE because of a signing error</a></h2>
+
+<p>The Android system requires that all applications be signed, as described in
+ <a href="{@docRoot}guide/publishing/app-signing.html">Signing Your Applications</a>. The ApiDemos
+applications included with the SDK are preinstalled on the emulator and for that reason have been
+compiled and signed with a private key.</p>
+
+If you want to modify or run one of the ApiDemos apps from Eclipse/ADT or other IDE, you can do so
+so only after you uninstall the <em>preinstalled</em> version of the app from the emulator. If
+you try to run an ApiDemos apps from your IDE without removing the preinstalled version first,
+you will get errors similar to: </p>
+
+<pre>[2008-08-13 15:14:15 - ApiDemos] Re-installation failed due to different application signatures.
+[2008-08-13 15:14:15 - ApiDemos] You must perform a full uninstall of the application. WARNING: ...This will remove the application data!
+[2008-08-13 15:14:15 - ApiDemos] Please execute 'adb uninstall com.android.samples' in a shell.</pre>
+
+<p>The error occurs because, in this case, you are attempting to install another copy of ApiDemos
+onto the emulator, a copy that is signed with a different certificate (the Android IDE tools will
+have signed the app with a debug certificate, where the existing version was already signed with
+a private certificate). The system does not allow this type of reinstallation. </p>
+
+<p>To resolve the issue, you need to fully uninstall the preinstalled and then reinstall it using
+the adb tool. Here's how to do that:</p>
+
+<ol>
+ <li>In a terminal, change to the tools directory of the SDK.</li>
+ <li>If no emulator instance is running, start an emulator using using the command <code>emulator &</code>.</li>
+ <li>Uninstall the preinstalled app using the command <code>adb uninstall com.android.samples</code>.</li>
+ <li>Reinstall the app using the command <code>adb install <path to the ApiDemos.apk></code>. If you are
+ working in Eclipse/ADT, you can just compile and run the app in the normal way. </li>
+</ol>
+
+<p>Note that if multiple emulator instances are running, you need to direct your uninstall/install
+commands to the emulator instance that you are targeting. To do that you can add the
+<code>-s <serialNumber></code> to the command, for example: </p>
+
+<pre>adb -s emulator-5556 install</pre>
+
+<p>For more information about adb, see the <a href="{@docRoot}guide/developing/tools/adb.html">Android Debug Bridge</a>
+documentation.</p>
+
+
+<h2 id="signingcalendar">I can't compile my app because the build tools generated an expired debug certificate</h2>
+
+<p>If your development machine uses a locale that has a non-Gregorian calendar, you may encounter problems when first trying to compile and run your application. Specifically, you may find that the Android build tools won't compile your application because the debug key is expired. </p>
+
+<p>The problem occurs because the Keytool utility — included in the JDK and used by the Android build tools — fails to properly handle non-Gregorian locales and may create validity dates that are in the past. That is, it may generate a debug key that is already expired, which results in the compile error.</p>
+
+<p>If you encounter this problem, follow these steps to work around it: </p>
+
+<ol>
+<li>First, delete the debug keystore/key already generated by the Android build tools. Specifically, delete the <code>debug.keystore</code> file. On Linux/Mac OSX, the file is stored in <code>~/.android</code>. On Windows XP, the file is stored in <code>
+C:\Documents and Settings\<user>\.android</code>. On Windows Vista, the file is stored in <code>
+C:\Users\<user>\.android</code></li>
+<li>Next, you can either
+<ul>
+<li>Temporarily change your development machine's locale (date and time) to one that uses a Gregorian calendar, for example, United States. Once the locale is changed, use the Android build tools to compile and install your app. The build tools will regenerate a new keystore and debug key with valid dates. Once the new debug key is generated, you can reset your development machine to the original locale. </li>
+<li>Alternatively, if you do not want to change your machine's locale settings, you can generate the keystore/key on any machine using the Gregorian calendar, then copy the <code>debug.keystore</code> file from that computer to the proper location on your development machine. </li>
+</ul>
+</li>
+</ol>
+
+<p>This problem has been verified on Windows and may apply to other platforms. </p>
+
+<p>For general information about signing Android applications, see
+<a href="{@docRoot}guide/publishing/app-signing.html">Signing Your Applications</a>. </p>
+
diff --git a/docs/html/resources/index.jd b/docs/html/resources/index.jd
new file mode 100644
index 0000000..5174dee
--- /dev/null
+++ b/docs/html/resources/index.jd
@@ -0,0 +1,38 @@
+page.title=Developer Resources
+@jd:body
+
+<p>
+This section provides technical articles, tutorials, sample code, and other
+information to help you quickly implement the features you want in your
+application.
+</p>
+
+<dl>
+<dt><b>Technical Articles</b></dt>
+<dd>Focused discussions about Android development subjects, including
+optimizations, tips, interesting implementations,
+and so on. Most of the articles provide "how-to" instructions for adding
+features or functionality to your app. The articles are drawn from posts to the
+Android Developers Blog.
+</dd>
+
+<dt><b>Tutorials</b></dt>
+<dd>Step-by-step instructions demonstrating how to build an Android application
+that has the specific features you want. </dd>
+
+<dt><b>Sample Code</b></dt>
+<dd>Fully-functioning sample applications that you can look at or build and run,
+to learn about how Android works. Feel free to reuse any of the code or
+techniques that you find in the samples!</dd>
+
+<dt><b>Community</b></dt>
+<dd>Links to the Android discussion groups and information about other ways to
+collaborate with other developers. </dd>
+
+<dt><b>More</b></dt>
+<dd>Quick development tips, troubleshooting information, and frequently asked
+questions (FAQs). </dd>
+</dl>
+
+<p>To return to this page later, just click the "Resources" tab while any
+Resources page is loaded. </p>
\ No newline at end of file
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
new file mode 100644
index 0000000..60d044a
--- /dev/null
+++ b/docs/html/resources/resources_toc.cs
@@ -0,0 +1,242 @@
+<ul>
+ <li>
+ <h2><span class="en">Community</span>
+ <span style="display:none" class="de"></span>
+ <span style="display:none" class="es">Comunidad</span>
+ <span style="display:none" class="fr">Communauté</span>
+ <span style="display:none" class="it"></span>
+ <span style="display:none" class="ja">コミュニティ</span>
+ <span style="display:none" class="zh-CN">社区</span>
+ <span style="display:none" class="zh-TW">社群</span>
+ </h2>
+ <ul>
+ <li><a href="<?cs var:toroot ?>resources/community-groups.html">
+ <span class="en">Android Developer Groups</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/community-more.html">
+ <span class="en">IRC, Twitter</span>
+ </a></li>
+ </ul>
+ </li>
+
+ <li>
+ <h2><span class="en">Technical Articles</span>
+ </h2>
+ <ul>
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>resources/articles/index.html">
+ <span class="en">List of Articles</span>
+ </a> <span class="new">new!</span></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>resources/articles/avoiding-memory-leaks.html">
+ <span class="en">Avoiding Memory Leaks</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/backward-compatibility.html">
+ <span class="en">Backward Compatibility</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/can-i-use-this-intent.html">
+ <span class="en">Can I Use this Intent?</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/creating-input-method.html">
+ <span class="en">Creating an Input Method</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/drawable-mutations.html">
+ <span class="en">Drawable Mutations</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/faster-screen-orientation-change.html">
+ <span class="en">Faster Screen Orientation Change</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/future-proofing.html">
+ <span class="en">Future-Proofing Your Apps</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/gestures.html">
+ <span class="en">Gestures</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/glsurfaceview.html">
+ <span class="en">Introducing GLSurfaceView</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/layout-tricks-reuse.html">
+ <span class="en">Layout Tricks: Reusing </span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/layout-tricks-efficiency.html">
+ <span class="en">Layout Tricks: Efficiency</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/layout-tricks-stubs.html">
+ <span class="en">Layout Tricks: ViewStubs </span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/layout-tricks-merge.html">
+ <span class="en">Layout Tricks: Merging </span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/listview-backgrounds.html">
+ <span class="en">ListView Backgrounds</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/live-folders.html">
+ <span class="en">Live Folders</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/on-screen-inputs.html">
+ <span class="en">Onscreen Input Methods</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/painless-threading.html">
+ <span class="en">Painless Threading</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/qsb.html">
+ <span class="en">Quick Search Box</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/touch-mode.html">
+ <span class="en">Touch Mode</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/track-mem.html">
+ <span class="en">Tracking Memory Allocations</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/ui-1.5.html">
+ <span class="en">UI Framework Changes in Android 1.5</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/ui-1.6.html">
+ <span class="en">UI Framework Changes in Android 1.6</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/timed-ui-updates.html">
+ <span class="en">Updating the UI from a Timer</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/tts.html">
+ <span class="en">Using Text-to-Speech</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/using-webviews.html">
+ <span class="en">Using WebViews</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/wikinotes-linkify.html">
+ <span class="en">WikiNotes: Linkify your Text!</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/wikinotes-intents.html">
+ <span class="en">WikiNotes: Routing Intents</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/window-bg-speed.html">
+ <span class="en">Window Backgrounds & UI Speed</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/articles/zipalign.html">
+ <span class="en">Zipalign: An Easy Optimization</span>
+ </a></li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+
+ <li>
+ <h2><span class="en">Tutorials</span>
+ <span class="de" style="display:none">Lernprogramme</span>
+ <span class="es" style="display:none">Tutoriales</span>
+ <span class="fr" style="display:none">Didacticiels</span>
+ <span class="it" style="display:none">Esercitazioni</span>
+ <span class="ja" style="display:none">チュートリアル</span>
+ <span class="zh-CN" style="display:none"></span>
+ <span class="zh-TW" style="display:none"></span>
+ </h2>
+ <ul>
+ <li><a href="<?cs var:toroot ?>resources/tutorials/hello-world.html">
+ <span class="en">Hello World</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/tutorials/views/index.html">
+ <span class="en">Hello Views</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/tutorials/localization/index.html">
+ <span class="en">Hello Localization</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/tutorials/notepad/index.html">
+ <span class="en">Notepad Tutorial</span>
+ </a></li>
+ </ul>
+ </li>
+
+
+ <li>
+ <h2><span class="en">Sample Code</span>
+ <span class="de" style="display:none">Beispielcode</span>
+ <span class="es" style="display:none">Código de ejemplo</span>
+ <span class="fr" style="display:none">Exemple de code</span>
+ <span class="it" style="display:none">Codice di esempio</span>
+ <span class="ja" style="display:none">サンプル コード</span>
+ <span class="zh-CN" style="display:none"></span>
+ <span class="zh-TW" style="display:none"></span>
+ </h2>
+ <ul>
+ <li><a href="<?cs var:toroot ?>resources/samples/ApiDemos/index.html">
+ <span class="en">API Demos</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/BluetoothChat/index.html">
+ <span class="en">Bluetooth Chat</span>
+ </a> <span class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/ContactManager/index.html">
+ <span class="en">Contact Manager</span>
+ </a> <span class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/Home/index.html">
+ <span class="en">Home</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/JetBoy/index.html">
+ <span class="en">JetBoy</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/LunarLander/index.html">
+ <span class="en">Lunar Lander</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/MultiResolution/index.html">
+ <span class="en">Multiple Resolutions</span>
+ </a> <span class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/NotePad/index.html">
+ <span class="en">Note Pad</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/SearchableDictionary/index.html">
+ <span class="en">Searchable Dictionary</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/Snake/index.html">
+ <span class="en">Snake</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/SoftKeyboard/index.html">
+ <span class="en">Soft Keyboard</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/Wiktionary/index.html">
+ <span class="en">Wiktionary</span>
+ </a> <span class="new">new!</span></li>
+ <li><a href="<?cs var:toroot ?>resources/samples/WiktionarySimple/index.html">
+ <span class="en">Wiktionary (Simplified)</span>
+ </a> <span class="new">new!</span></li>
+ </ul>
+ </li>
+
+
+
+ <li>
+ <h2><span class="en">More</span>
+ </h2>
+ <ul>
+ <li><a href="<?cs var:toroot ?>resources/faq/commontasks.html">
+ <span class="en">Common Tasks </span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/faq/troubleshooting.html">
+ <span class="en">Troubleshooting Tips</span>
+ </a></li>
+ <li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>resources/faq/index.html">
+ <span class="en">FAQs</span>
+ </a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>resources/faq/framework.html">
+ <span class="en">App Framework FAQ</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/faq/licensingandoss.html">
+ <span class="en">Licensing FAQ</span>
+ </a></li>
+ <li><a href="<?cs var:toroot ?>resources/faq/security.html">
+ <span class="en">Security FAQ</span>
+ </a></li>
+ </ul>
+ </li>
+ </ul>
+ </li>
+
+
+</ul>
+
+<script type="text/javascript">
+<!--
+ buildToggleLists();
+ changeNavLang(getLangPref());
+//-->
+</script>
diff --git a/docs/html/resources/samples/images/BluetoothChat1.png b/docs/html/resources/samples/images/BluetoothChat1.png
new file mode 100644
index 0000000..f87da6a
--- /dev/null
+++ b/docs/html/resources/samples/images/BluetoothChat1.png
Binary files differ
diff --git a/docs/html/resources/samples/images/BluetoothChat2.png b/docs/html/resources/samples/images/BluetoothChat2.png
new file mode 100644
index 0000000..6218eff
--- /dev/null
+++ b/docs/html/resources/samples/images/BluetoothChat2.png
Binary files differ
diff --git a/docs/html/resources/samples/images/ContactManager1.png b/docs/html/resources/samples/images/ContactManager1.png
new file mode 100644
index 0000000..d787ffd
--- /dev/null
+++ b/docs/html/resources/samples/images/ContactManager1.png
Binary files differ
diff --git a/docs/html/resources/samples/images/ContactManager2.png b/docs/html/resources/samples/images/ContactManager2.png
new file mode 100644
index 0000000..f783749
--- /dev/null
+++ b/docs/html/resources/samples/images/ContactManager2.png
Binary files differ
diff --git a/docs/html/resources/samples/images/HomeSample.png b/docs/html/resources/samples/images/HomeSample.png
new file mode 100644
index 0000000..990bebb
--- /dev/null
+++ b/docs/html/resources/samples/images/HomeSample.png
Binary files differ
diff --git a/docs/html/resources/samples/images/JetBoy.png b/docs/html/resources/samples/images/JetBoy.png
new file mode 100644
index 0000000..3da0448
--- /dev/null
+++ b/docs/html/resources/samples/images/JetBoy.png
Binary files differ
diff --git a/docs/html/resources/samples/images/MultiResolution.png b/docs/html/resources/samples/images/MultiResolution.png
new file mode 100644
index 0000000..8b245d9
--- /dev/null
+++ b/docs/html/resources/samples/images/MultiResolution.png
Binary files differ
diff --git a/docs/html/resources/samples/images/SearchableDictionary1.png b/docs/html/resources/samples/images/SearchableDictionary1.png
new file mode 100644
index 0000000..ebb4604
--- /dev/null
+++ b/docs/html/resources/samples/images/SearchableDictionary1.png
Binary files differ
diff --git a/docs/html/resources/samples/images/SearchableDictionary2.png b/docs/html/resources/samples/images/SearchableDictionary2.png
new file mode 100644
index 0000000..34746cd
--- /dev/null
+++ b/docs/html/resources/samples/images/SearchableDictionary2.png
Binary files differ
diff --git a/docs/html/resources/samples/images/Snake.png b/docs/html/resources/samples/images/Snake.png
new file mode 100644
index 0000000..c5211d8
--- /dev/null
+++ b/docs/html/resources/samples/images/Snake.png
Binary files differ
diff --git a/docs/html/resources/samples/images/SoftKeyboard.png b/docs/html/resources/samples/images/SoftKeyboard.png
new file mode 100644
index 0000000..8a4ec63
--- /dev/null
+++ b/docs/html/resources/samples/images/SoftKeyboard.png
Binary files differ
diff --git a/docs/html/resources/samples/images/Wiktionary.png b/docs/html/resources/samples/images/Wiktionary.png
new file mode 100644
index 0000000..78fee7c
--- /dev/null
+++ b/docs/html/resources/samples/images/Wiktionary.png
Binary files differ
diff --git a/docs/html/resources/samples/images/WiktionarySimple.png b/docs/html/resources/samples/images/WiktionarySimple.png
new file mode 100644
index 0000000..57cd11d
--- /dev/null
+++ b/docs/html/resources/samples/images/WiktionarySimple.png
Binary files differ
diff --git a/docs/html/resources/samples/images/sample_lunarlander.png b/docs/html/resources/samples/images/sample_lunarlander.png
new file mode 100644
index 0000000..a2ff75a
--- /dev/null
+++ b/docs/html/resources/samples/images/sample_lunarlander.png
Binary files differ
diff --git a/docs/html/resources/samples/images/sample_note.png b/docs/html/resources/samples/images/sample_note.png
new file mode 100644
index 0000000..8fc9dcc
--- /dev/null
+++ b/docs/html/resources/samples/images/sample_note.png
Binary files differ
diff --git a/docs/html/resources/samples/images/sample_notepad.png b/docs/html/resources/samples/images/sample_notepad.png
new file mode 100644
index 0000000..46f2211
--- /dev/null
+++ b/docs/html/resources/samples/images/sample_notepad.png
Binary files differ
diff --git a/docs/html/resources/samples/index.jd b/docs/html/resources/samples/index.jd
new file mode 100644
index 0000000..7fbbea1
--- /dev/null
+++ b/docs/html/resources/samples/index.jd
@@ -0,0 +1,72 @@
+guide=true
+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>
+
+<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 style="margin-left:2em">
+<code><em><sdk></em>/platforms/android-<em><version></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><target_ID></em> -p <em><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>
+
+<dl>
+
+ <dt><a href="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="Home/index.html">Home</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="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="LunarLander/index.html">Lunar Lander</a></dt>
+ <dd>A classic Lunar Lander game.</dd>
+
+ <dt><a href="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="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="Snake/index.html">Snake</a></dt>
+ <dd>An implementation of the classic game "Snake."</dd>
+
+ <dt><a href="SoftKeyboard/index.html">Soft Keyboard</a></dt>
+ <dd>An example of writing an input method for a software keyboard.</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/resources/tutorials/hello-world.jd b/docs/html/resources/tutorials/hello-world.jd
new file mode 100644
index 0000000..58d1a16
--- /dev/null
+++ b/docs/html/resources/tutorials/hello-world.jd
@@ -0,0 +1,564 @@
+page.title=Hello, World
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#avd">Create an AVD</a></li>
+ <li><a href="#create">Create the Project</a></li>
+ <li><a href="#ui">Construct the UI</a></li>
+ <li><a href="#run">Run the Code</a></li>
+ <li><a href="#upgrading">Upgrade the UI to an XML Layout</a></li>
+ <li><a href="#debugging">Debug Your Project</a></li>
+ <li><a href="#noeclipse">Creating the Project Without Eclipse</a></li>
+ </ol>
+ </div>
+</div>
+
+<p>As a developer, you know that the first impression
+of a development framework is how easy it is to write "Hello,
+World." Well, on Android, it's pretty easy.
+It's particularly easy if you're using Eclipse as your IDE, because we've provided a
+great plugin that handles your project creation and management to greatly speed-up your
+development cycles.</p>
+
+<p>If you're not using Eclipse, that's okay. Familiarize yourself with
+<a href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>.
+You can then return to this tutorial and ignore anything about Eclipse.</p>
+
+<p>Before you start, you should already have the very latest SDK installed, and if you're using
+Eclipse, you should have installed the ADT plugin as well. If you have not installed these, see
+<a href="{@docRoot}sdk/installing.html">Installing the Android SDK</a> and return
+here when you've completed the installation.</p>
+
+<h2 id="avd">Create an AVD</h2>
+
+<div class="sidebox-wrapper">
+ <div class="sidebox-inner">
+ <p>To learn more about how to use AVDs and the options
+ available to you, refer to the
+ <a href="{@docRoot}guide/developing/tools/avd.html">Android
+ Virtual Devices</a> document.</p>
+ </div>
+</div>
+
+<p>In this tutorial, you will run your application in the Android Emulator.
+Before you can launch the emulator, you must create an
+Android Virtual Device (AVD). An AVD defines the system image and
+device settings used by the emulator.</p>
+
+<p>To create an AVD, use the "android" tool provided in the Android SDK.
+Open a command prompt or terminal, navigate to the
+<code>tools/</code> directory in the SDK package and execute:
+<pre>
+android create avd --target 2 --name my_avd
+</pre>
+
+<p>The tool now asks if you would like to create a custom hardware profile.
+For the time being, press Return to skip it ("no" is the default response).
+That's it. This configures an AVD named "my_avd" that uses the Android 1.5
+platform. The AVD is now ready for use in the emulator.</p>
+
+<p>In the above command, the <code>--target</code> option is required
+and specifies the deployment target to run on the emulator.
+The <code>--name</code> option is also required and defines the
+name for the new AVD.</p>
+
+
+<h2 id="create">Create a New Android Project</h2>
+
+<p>After you've created an AVD, the next step is to start a new
+Android project in Eclipse.</p>
+
+<ol>
+ <li>From Eclipse, select <strong>File > New > Project</strong>.
+ <p>If the ADT
+ Plugin for Eclipse has been successfully installed, the resulting dialog
+ should have a folder labeled "Android" which should contain
+ "Android Project". (After you create one or more Android projects, an entry for
+ "Android XML File" will also be available.)</p>
+ </li>
+
+ <li>Select "Android Project" and click <strong>Next</strong>.<br/>
+ <a href="images/hello_world_0.png"><img src="images/hello_world_0.png" style="height:230px" alt="" /></a>
+ </li>
+
+ <li>Fill in the project details with the following values:
+ <ul>
+ <li><em>Project name:</em> HelloAndroid</li>
+ <li><em>Application name:</em> Hello, Android</li>
+ <li><em>Package name:</em> com.example.helloandroid (or your own private namespace)</li>
+ <li><em>Create Activity:</em> HelloAndroid</li>
+ <li><em>Min SDK Version:</em> 2</li>
+ </ul>
+ <p>Click <strong>Finish</strong>.</p>
+
+ <a href="images/hello_world_1.png"><img src="images/hello_world_1.png" style="height:230px" alt="" /></a>
+
+ <p>Here is a description of each field:</p>
+
+ <dl>
+ <dt><em>Project Name</em></dt>
+ <dd>This is the Eclipse Project name — the name of the directory
+ that will contain the project files.</dd>
+ <dt><em>Application Name</em></dt>
+ <dd>This is the human-readable title for your application — the name that
+ will appear on the Android device.</dd>
+ <dt><em>Package Name</em></dt>
+ <dd>This is the package namespace (following the same rules as for
+ packages in the Java programming language) that you want all your source code to
+ reside under. This also sets the package name under which the stub
+ Activity will be generated.
+ <p>Your package name must be unique across
+ all packages installed on the Android system; for this reason, it's very
+ important to use a standard domain-style package for your
+ applications. The example above uses the "com.example" namespace, which is
+ a namespace reserved for example documentation —
+ when you develop your own applications, you should use a namespace that's
+ appropriate to your organization or entity.</p></dd>
+ <dt><em>Create Activity</em></dt>
+ <dd>This is the name for the class stub that will be generated by the plugin.
+ This will be a subclass of Android's {@link android.app.Activity} class. An
+ Activity is simply a class that can run and do work. It can create a UI if it
+ chooses, but it doesn't need to. As the checkbox suggests, this is optional, but an
+ Activity is almost always used as the basis for an application.</dd>
+ <dt><em>Min SDK Version</em></dt>
+ <dd>This value specifies the minimum API Level required by your application. If the API Level
+ entered here matches the API Level provided by one of the available targets,
+ then that Build Target will be automatically selected (in this case, entering
+ "2" as the API Level will select the Android 1.1 target). With each new
+ version of the Android system image and Android SDK, there have likely been
+ additions or changes made to the APIs. When this occurs, a new API Level is assigned
+ to the system image to regulate which applications are allowed to be run. If an
+ application requires an API Level that is <em>higher</em> than the level supported
+ by the device, then the application will not be installed.</dd>
+ </dl>
+
+ <p><em>Other fields</em>: The checkbox for "Use default location" allows you to change
+ the location on disk where the project's files will be generated and stored. "Build Target"
+ is the platform target that your application will be compiled against
+ (this should be selected automatically, based on your Min SDK Version).</p>
+
+ <p class="note">Notice that the "Build Target" you've selected uses the Android 1.1
+ platform. This means that your application will be compiled against the Android 1.1
+ platform library. If you recall, the AVD created above runs on the Android 1.5 platform.
+ These don't have to match; Android applications are forward-compatible, so an application
+ built against the 1.1 platform library will run normally on the 1.5 platform. The reverse
+ is not true.</p>
+ </li>
+</ol>
+
+<p>Your Android project is now ready. It should be visible in the Package
+Explorer on the left.
+Open the <code>HelloAndroid.java</code> file, located inside <em>HelloAndroid > src >
+com.example.helloandroid</em>). It should look like this:</p>
+
+<pre>
+package com.example.helloandroid;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class HelloAndroid extends Activity {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ }
+}</pre>
+
+<p>Notice that the class is based on the {@link android.app.Activity} class. An Activity is a
+single application entity that is used to perform actions. An application may have many separate
+activities, but the user interacts with them one at a time. The
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method
+will be called by the Android system when your Activity starts —
+it is where you should perform all initialization and UI setup. An activity is not required to
+have a user interface, but usually will.</p>
+
+<p>Now let's modify some code! </p>
+
+
+<h2 id="ui">Construct the UI</h2>
+
+<p>Take a look at the revised code below and then make the same changes to your HelloAndroid class.
+The bold items are lines that have been added.</p>
+
+<pre>
+package com.android.helloandroid;
+
+import android.app.Activity;
+import android.os.Bundle;
+<strong>import android.widget.TextView;</strong>
+
+public class HelloAndroid extends Activity {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ <strong>TextView tv = new TextView(this);
+ tv.setText("Hello, Android");
+ setContentView(tv);</strong>
+ }
+}</pre>
+
+<p class="note"><strong>Tip:</strong> An easy way to add import packages to your project is
+to press <strong>Ctrl-Shift-O</strong> (<strong>Cmd-Shift-O</strong>, on Mac). This is an Eclipse
+shortcut that identifies missing packages based on your code and adds them for you.</p>
+
+<p>An Android user interface is composed of hierarchies of objects called
+Views. A {@link android.view.View} is a drawable object used as an element in your UI layout,
+such as a button, image, or (in this case) a text label. Each of these objects is a subclass
+of the View class and the subclass that handles text is {@link android.widget.TextView}.</p>
+
+<p>In this change, you create a TextView with the class constructor, which accepts
+an Android {@link android.content.Context} instance as its parameter. A
+Context is a handle to the system; it provides services like
+resolving resources, obtaining access to databases and preferences, and so
+on. The Activity class inherits from Context, and because your
+HelloAndroid class is a subclass of Activity, it is also a Context. So, you can
+pass <code>this</code> as your Context reference to the TextView.</p>
+
+<p>Next, you define the text content with
+{@link android.widget.TextView setText(CharSequence) setText()}.</p>
+
+<p>Finally, you pass the TextView to
+{@link android.app.Activity#setContentView(View) setContentView()} in order to
+display it as the content for the Activity UI. If your Activity doesn't
+call this method, then no UI is present and the system will display a blank
+screen.</p>
+
+<p>There it is — "Hello, World" in Android! The next step, of course, is
+to see it running.</p>
+
+
+<h2 id="run">Run the Application</h2>
+
+<p>The Eclipse plugin makes it very easy to run your applications:</p>
+
+<ol>
+ <li>Select <strong>Run > Run</strong>.</li>
+ <li>Select "Android Application".</li>
+</ol>
+
+<div class="sidebox-wrapper">
+ <div class="sidebox-inner">
+ <p>To learn more about creating and editing run configurations in Eclipse, refer to
+ <a href="{@docRoot}guide/developing/eclipse-adt.html#RunConfig">Developing In Eclipse,
+ with ADT</a>.</p>
+ </div>
+</div>
+
+<p>The Eclipse ADT will automatically create a new run configuration for your project
+and the Android Emulator will automatically launch. Once the emulator is booted up,
+your application will appear after a moment. You should now see something like this:</p>
+
+ <a href="images/hello_world_5.png"><img src="images/hello_world_5.png" style="height:230px" alt="" /></a>
+
+<p>The "Hello, Android" you see in the grey bar is actually the application title. The Eclipse plugin
+creates this automatically (the string is defined in the <code>res/values/strings.xml</code> file and referenced
+by your <code>AndroidManifest.xml</code> file). The text below the title is the actual text that you have
+created in the TextView object.</p>
+
+<p>That concludes the basic "Hello World" tutorial, but you should continue reading for some more
+valuable information about developing Android applications.</p>
+
+
+<h2 id="upgrading">Upgrade the UI to an XML Layout</h2>
+
+<p>The "Hello, World" example you just completed uses what is called a "programmatic"
+UI layout. This means that you constructed and built your application's UI
+directly in source code. If you've done much UI programming, you're
+probably familiar with how brittle that approach can sometimes be: small
+changes in layout can result in big source-code headaches. It's also very
+easy to forget to properly connect Views together, which can result in errors in
+your layout and wasted time debugging your code.</p>
+
+<p>That's why Android provides an alternate UI construction model: XML-based
+layout files. The easiest way to explain this concept is to show an
+example. Here's an XML layout file that is identical in behavior to the
+programmatically-constructed example:</p>
+
+<pre><?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:text="@string/hello"/></pre>
+
+<p>The general structure of an Android XML layout file is simple: it's a tree
+of XML elements, wherein each node is the name of a View class
+(this example, however, is just one View element). You can use the
+name of any class that extends {@link android.view.View} as an element in your XML layouts,
+including custom View classes you define in your own code. This
+structure makes it very easy to quickly build up UIs, using a more simple
+structure and syntax than you would use in a programmatic layout. This model is inspired
+by the web development model, wherein you can separate the presentation of your
+application (its UI) from the application logic used to fetch and fill in data.</p>
+
+<p>In the above XML example, there's just one View element: the <code>TextView</code>,
+which has four XML attributes. Here's a summary of what they mean:</p>
+
+<table>
+ <tbody>
+ <tr>
+ <th>
+ Attribute
+ </th>
+ <th>
+ Meaning
+ </th>
+ </tr>
+ <tr>
+ <td>
+ <code>xmlns:android</code>
+ </td>
+ <td>
+ This is an XML namespace declaration that tells the Android tools that you are going to refer to common attributes defined in the Android namespace. The outermost tag in every Android layout file must have this attribute.<br>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>android:layout_width</code>
+ </td>
+ <td>
+ This attribute defines how much of the available width on the screen this View should consume.
+In this case, it's the only View so you want it to take up the entire screen, which is what a value of "fill_parent" means.<br>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>android:layout_height</code>
+ </td>
+ <td>
+ This is just like android:layout_width, except that it refers to available screen height.
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <code>android:text</code>
+ </td>
+ <td>
+ This sets the text that the TextView should display. In this example, you use a string
+ resource instead of a hard-coded string value.
+ The <em>hello</em> string is defined in the <em>res/values/strings.xml</em> file. This is the
+ recommended practice for inserting strings to your application, because it makes the localization
+ of your application to other languages graceful, without need to hard-code changes to the layout file.
+ For more information, see <a href="{@docRoot}guide/topics/resources/resources-i18n.html">Resources
+ and Internationalization</a>.
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+<p>These XML layout files belong in the <code>res/layout/</code> directory of your project. The "res" is
+short for "resources" and the directory contains all the non-code assets that
+your application requires. In addition to layout files, resources also include assets
+such as images, sounds, and localized strings.</p>
+
+<div class="sidebox">
+ <h2>Landscape layout</h2>
+ <p>When you want a different design for landscape, put your layout XML file
+ inside /res/layout-land. Android will automatically look here when the layout changes.
+ Without this special landscape layout defined, Android will stretch the default layout.</p>
+</div>
+
+<p>The Eclipse plugin automatically creates one of these layout files for you: main.xml.
+In the "Hello World" application you just completed, this file was ignored and you created a
+layout programmatically. This was meant to teach you more
+about the Android framework, but you should almost always define your layout
+in an XML file instead of in your code.
+The following procedures will instruct you how to change your
+existing application to use an XML layout.</p>
+
+<ol>
+ <li>In the Eclipse Package Explorer, expand the
+<code>/res/layout/</code> folder and open <code>main.xml</code> (once opened, you might need to click
+the "main.xml" tab at the bottom of the window to see the XML source). Replace the contents with
+the following XML:
+
+<pre><?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:text="@string/hello"/></pre>
+<p>Save the file.</p>
+</li>
+
+<li>Inside the <code>res/values/</code> folder, open <code>strings.xml</code>.
+This is where you should save all default text strings for your user interface. If you're using Eclipse, then
+ADT will have started you with two strings, <em>hello</em> and <em>app_name</em>.
+Revise <em>hello</em> to something else. Perhaps "Hello, Android! I am a string resource!"
+The entire file should now look like this:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="hello">Hello, Android! I am a string resource!</string>
+ <string name="app_name">Hello, Android</string>
+</resources>
+</pre>
+</li>
+
+<li>Now open and modify your <code>HelloAndroid</code> class use the
+XML layout. Edit the file to look like this:
+<pre>
+package com.example.helloandroid;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class HelloAndroid extends Activity {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ }
+}</pre>
+
+<p>When you make this change, type it by hand to try the
+code-completion feature. As you begin typing "R.layout.main" the plugin will offer you
+suggestions. You'll find that it helps in a lot of situations.</p>
+
+<p>Instead of passing <code>setContentView()</code> a View object, you give it a reference
+to the layout resource.
+The resource is identified as <code>R.layout.main</code>, which is actually a compiled object representation of
+the layout defined in <code>/res/layout/main.xml</code>. The Eclipse plugin automatically creates this reference for
+you inside the project's R.java class. If you're not using Eclipse, then the R.java class will be generated for you
+when you run Ant to build the application. (More about the R class in a moment.)</p>
+</li>
+</ol>
+
+<p>Now re-run your application — because you've created a launch configuration, all
+you need to do is click the green arrow icon to run, or select
+<strong>Run > Run History > Android Activity</strong>. Other than the change to the TextView
+string, the application looks the same. After all, the point was to show that the two different
+layout approaches produce identical results.</p>
+
+<p class="note"><strong>Tip:</strong> Use the shortcut <strong>Ctrl-F11</strong>
+(<strong>Cmd-Shift-F11</strong>, on Mac) to run your currently visible application.</p>
+
+<p>Continue reading for an introduction
+to debugging and a little more information on using other IDEs. When you're ready to learn more,
+read <a href="{@docRoot}guide/topics/fundamentals.html">Application
+Fundamentals</a> for an introduction to all the elements that make Android applications work.
+Also refer to the <a href="{@docRoot}guide/index.html">Developer's Guide</a>
+introduction page for an overview of the <em>Dev Guide</em> documentation.</p>
+
+
+<div class="special">
+<h3>R class</h3>
+<p>In Eclipse, open the file named <code>R.java</code> (in the <code>gen/</code> [Generated Java Files] folder).
+It should look something like this:</p>
+
+<pre>
+package com.example.helloandroid;
+
+public final class R {
+ public static final class attr {
+ }
+ public static final class drawable {
+ public static final int icon=0x7f020000;
+ }
+ public static final class layout {
+ public static final int main=0x7f030000;
+ }
+ public static final class string {
+ public static final int app_name=0x7f040001;
+ public static final int hello=0x7f040000;
+ }
+}
+</pre>
+
+<p>A project's <code>R.java</code> file is an index into all the resources defined in the
+file. You use this class in your source code as a sort of short-hand
+way to refer to resources you've included in your project. This is
+particularly powerful with the code-completion features of IDEs like Eclipse
+because it lets you quickly and interactively locate the specific reference
+you're looking for.</p>
+
+<p>It's possible yours looks slighly different than this (perhaps the hexadecimal values are different).
+For now, notice the inner class named "layout", and its
+member field "main". The Eclipse plugin noticed the XML
+layout file named main.xml and generated a class for it here. As you add other
+resources to your project (such as strings in the <code>res/values/string.xml</code> file or drawables inside
+the <code>res/drawable/</code> direcory) you'll see <code>R.java</code> change to keep up.</p>
+<p>When not using Eclipse, this class file will be generated for you at build time (with the Ant tool).</p>
+<p><em>You should never edit this file by hand.</em></p>
+</div>
+
+<h2 id="debugging">Debug Your Project</h2>
+
+<p>The Android Plugin for Eclipse also has excellent integration with the Eclipse
+debugger. To demonstrate this, introduce a bug into
+your code. Change your HelloAndroid source code to look like this:</p>
+
+<pre>
+package com.android.helloandroid;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class HelloAndroid extends Activity {
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Object o = null;
+ o.toString();
+ setContentView(R.layout.main);
+ }
+}</pre>
+
+<p>This change simply introduces a NullPointerException into your code. If
+you run your application again, you'll eventually see this:</p>
+
+ <a href="images/hello_world_8.png"><img src="images/hello_world_8.png" style="height:230px" alt="" /></a>
+
+<p>Press "Force Quit" to terminate the application and close the emulator window.</p>
+
+<p>To find out more about the error, set a breakpoint in your source code
+on the line <code>Object o = null;</code> (double-click on the marker bar next to the source code line). Then select <strong>Run > Debug History > Hello,
+Android</strong> from the menu to enter debug mode. Your app will restart in the
+emulator, but this time it will suspend when it reaches the breakpoint you
+set. You can then step through the code in Eclipse's Debug Perspective,
+just as you would for any other application.</p>
+
+ <a href="images/hello_world_9.png"><img src="images/hello_world_9.png" style="height:230px" alt="" /></a>
+
+
+<h2 id="noeclipse">Creating the Project without Eclipse</h2>
+
+ <p>If you don't use Eclipse (such as if you prefer another IDE, or simply use text
+ editors and command line tools) then the Eclipse plugin can't help you.
+ Don't worry though — you don't lose any functionality just because you don't
+ use Eclipse.</p>
+
+ <p>The Android Plugin for Eclipse is really just a wrapper around a set of tools
+ included with the Android SDK. (These tools, like the emulator, aapt, adb,
+ ddms, and others are <a href="{@docRoot}guide/developing/tools/index.html">documented elsewhere.</a>)
+ Thus, it's possible to
+ wrap those tools with another tool, such as an 'ant' build file.</p>
+
+ <p>The Android SDK includes a tool named "android" that can be
+ used to create all the source code and directory stubs for your project, as well
+ as an ant-compatible <code>build.xml</code> file. This allows you to build your project
+ from the command line, or integrate it with the IDE of your choice.</p>
+
+ <p>For example, to create a HelloAndroid project similar to the one created
+ in Eclipse, use this command:</p>
+
+ <pre>
+android create project \
+ --package com.android.helloandroid \
+ --activity HelloAndroid \
+ --target 2 \
+ --path <em><path-to-your-project></em>/HelloAndroid
+</pre>
+
+ <p>This creates the required folders and files for the project at the location
+ defined by the <em>path</em>.</p>
+
+ <p>For more information on how to use the SDK tools to create and build projects, please read
+<a href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>.</p>
diff --git a/docs/html/resources/tutorials/images/hello_world_0.png b/docs/html/resources/tutorials/images/hello_world_0.png
new file mode 100644
index 0000000..330a07c
--- /dev/null
+++ b/docs/html/resources/tutorials/images/hello_world_0.png
Binary files differ
diff --git a/docs/html/resources/tutorials/images/hello_world_1.png b/docs/html/resources/tutorials/images/hello_world_1.png
new file mode 100644
index 0000000..1e5f7b0
--- /dev/null
+++ b/docs/html/resources/tutorials/images/hello_world_1.png
Binary files differ
diff --git a/docs/html/resources/tutorials/images/hello_world_2.png b/docs/html/resources/tutorials/images/hello_world_2.png
new file mode 100644
index 0000000..3e9c58b
--- /dev/null
+++ b/docs/html/resources/tutorials/images/hello_world_2.png
Binary files differ
diff --git a/docs/html/resources/tutorials/images/hello_world_3.png b/docs/html/resources/tutorials/images/hello_world_3.png
new file mode 100644
index 0000000..22901a9
--- /dev/null
+++ b/docs/html/resources/tutorials/images/hello_world_3.png
Binary files differ
diff --git a/docs/html/resources/tutorials/images/hello_world_4.png b/docs/html/resources/tutorials/images/hello_world_4.png
new file mode 100644
index 0000000..5c41e80
--- /dev/null
+++ b/docs/html/resources/tutorials/images/hello_world_4.png
Binary files differ
diff --git a/docs/html/resources/tutorials/images/hello_world_5.png b/docs/html/resources/tutorials/images/hello_world_5.png
new file mode 100644
index 0000000..96b830a
--- /dev/null
+++ b/docs/html/resources/tutorials/images/hello_world_5.png
Binary files differ
diff --git a/docs/html/resources/tutorials/images/hello_world_8.png b/docs/html/resources/tutorials/images/hello_world_8.png
new file mode 100644
index 0000000..07db360
--- /dev/null
+++ b/docs/html/resources/tutorials/images/hello_world_8.png
Binary files differ
diff --git a/docs/html/resources/tutorials/images/hello_world_9.png b/docs/html/resources/tutorials/images/hello_world_9.png
new file mode 100644
index 0000000..a66526a
--- /dev/null
+++ b/docs/html/resources/tutorials/images/hello_world_9.png
Binary files differ
diff --git a/docs/html/resources/tutorials/index.html b/docs/html/resources/tutorials/index.html
new file mode 100644
index 0000000..4881acf
--- /dev/null
+++ b/docs/html/resources/tutorials/index.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<meta http-equiv="refresh" content="0;url=../index.html">
+</head>
+<body>
+<a href="../index.html">click here</a> if you are not redirected.
+</body>
+</html>
\ No newline at end of file
diff --git a/docs/html/resources/tutorials/localization/index.jd b/docs/html/resources/tutorials/localization/index.jd
new file mode 100755
index 0000000..8a60814
--- /dev/null
+++ b/docs/html/resources/tutorials/localization/index.jd
@@ -0,0 +1,593 @@
+page.title=Hello, L10N
+@jd:body
+
+<div id="qv-wrapper">
+ <div id="qv">
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#unlocalized">Create an Unlocalized App</a>
+ <ol>
+ <li><a href="#create">Create the Project and Layout</a></li>
+ <li><a href="#default">Create Default Resources</a></li>
+ </ol>
+ </li>
+ <li><a href="#run">Run the Unlocalized App</a></li>
+ <li><a href="#plan">Plan the Localization</a></li>
+ <li><a href="#localize">Localize the App</a>
+ <ol>
+ <li><a href="#localize_strings">Localize the Strings</a></li>
+ <li><a href="#localize_images">Localize the Images</a></li>
+ </ol>
+ </li>
+ <li><a href="#test_localized">Run and Test the Localized App</a></li>
+ </ol>
+ <h2>See also</h2>
+ <ol>
+<li>{@link android.widget.Button}</li>
+<li>{@link android.widget.TextView}</li>
+<li>{@link android.app.AlertDialog}</li>
+</ol>
+ </div>
+</div>
+
+<p>In this tutorial, we will create a Hello, L10N application that uses the
+Android framework to selectively load resources. Then we will localize the
+application by adding resources to the <code>res/</code> directory. </p>
+
+<p>This tutorial uses the practices described in the <a
+href="{@docRoot}guide/topics/resources/localization.html">Localization</a>
+document. </p>
+
+
+<h2 id="unlocalized">Create an Unlocalized Application</h2>
+
+<p>The first version of the Hello, L10N application will use only the default
+resource directories (<code>res/drawable</code>, <code>res/layout</code>, and
+<code>res/values</code>). These resources are not localized — they are the
+graphics, layout, and strings that we expect the application to use most often.
+When a user runs the application in the default locale, or in a locale that the
+application does not specifically support, the application will load resources
+from these default directories.</p>
+
+<p>The application consists of a simple user interface that displays two
+{@link android.widget.TextView} objects and a {@link android.widget.Button} image with a
+ background image of a national flag. When clicked, the button displays an
+{@link android.app.AlertDialog} object that shows additional text. </p>
+
+<h3 id="create">Create the Project and Layout</h3>
+
+<p>For this application, the default language will be British English and the
+default location the United Kingdom. </p>
+
+<ol>
+ <li>Start a new project and Activity called "HelloL10N." If you are
+using Eclipse, fill out these values in the New Android Project wizard:
+ <ul>
+ <li><em>Project name:</em> HelloL10N</li>
+ <li><em>Application name:</em> Hello, L10N</li>
+ <li><em>Package name:</em> com.example.hellol10n (or your own private
+namespace)</li>
+ <li><em>Create Activity:</em> HelloL10N</li>
+ <li><em>Min SDK Version:</em> 3</li>
+ </ul>
+ <p>The basic project contains a <code>res/</code> directory with
+subdirectories for the three most common types of resources: graphics
+(<code>res/drawable/</code>), layouts (<code>res/layout/</code>) and strings
+(<code>res/values/</code>). Most of the localization work you do later in this
+tutorial will involve adding more subdirectories to the <code>res/</code>
+directory.</p>
+ <img src="{@docRoot}images/hello_l10n/plain_project.png" alt="plain project" width="194"
+height="229">
+ </li>
+ <li>Open the <code>res/layout/main.xml</code> file and replace it with the
+following code:
+ <pre><?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/text_a"
+ />
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/text_b"
+ />
+<Button
+ android:id="@+id/flag_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ />
+</LinearLayout>
+ </pre>
+
+ <p>The LinearLayout has two {@link android.widget.TextView} objects that will
+display localized text and one {@link android.widget.Button} that shows a flag.
+</p>
+ </li>
+</ol>
+
+<h3 id="default">Create Default Resources</h3>
+
+<p>The layout refers to resources that need to be defined. </p>
+
+<ol>
+ <li>Create default text strings. To do this, open the <code>res/values/strings.xml</code> file and replace it with the following code:<br>
+ <pre><?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">Hello, L10N</string>
+ <string name="text_a">Shall I compare thee to a summer"'"s day?</string>
+ <string name="text_b">Thou art more lovely and more temperate.</string>
+ <string name="dialog_title">No Localisation</string>
+ <string name="dialog_text">This dialog box"'"s strings are not localised. For every locale, the text here will come from values/strings.xml.</string>
+</resources></pre>
+
+ <p>This code provides British English text for each string that the application
+will use. When we localize this application, we will provide alternate text in
+German, French, and Japanese for some of the strings.</p>
+ </li>
+ <li>Add a default flag graphic to the <code>res/drawable</code> folder by
+saving <a href="../../../images/hello_l10n/flag.png">flag.png</a> as
+<code>res/drawable/flag.png</code>. When the application is not localized, it
+will show a British flag.<br>
+
+ </li>
+ <li>Open HelloL10N.java (in the <code>src/</code> directory) and add the
+following code inside the <code>onCreate()</code> method (after
+<code>setContentView</code>).
+
+ <pre>// assign flag.png to the button, loading correct flag image for current locale
+Button b;
+(b = (Button)findViewById(R.id.flag_button)).setBackgroundDrawable(this.getResources().getDrawable(R.drawable.flag));
+
+// build dialog box to display when user clicks the flag
+AlertDialog.Builder builder = new AlertDialog.Builder(this);
+builder.setMessage(R.string.dialog_text)
+ .setCancelable(false)
+ .setTitle(R.string.dialog_title)
+ .setPositiveButton("Done", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.dismiss();
+ }
+ });
+final AlertDialog alert = builder.create();
+
+// set click listener on the flag to show the dialog box
+b.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ alert.show();
+ }
+ });</pre>
+
+ <p class="note"><strong>Tip:</strong> In Eclipse, use
+<strong>Ctrl-Shift-O</strong> (<strong>Cmd-Shift-O</strong>, on Mac) to find and
+add missing import packages to your project, then save the HelloL10N.java
+file.</p>
+
+ <p>The code that you added does the following:</p>
+
+ <ul>
+ <li>It assigns the correct flag icon to the button.
+ For now, no resources are defined other than the default, so this code
+will always assign the contents of <code>res/drawable/flag.png</code> (the
+British flag) as the flag icon, no matter what the locale. Once we add more
+flags for different locales, this code will sometimes assign a different flag.
+</li>
+ <li>It creates an {@link android.app.AlertDialog} object and sets a click listener so that when the
+user clicks the button, the AlertDialog will display.
+ We will not localize the dialog text;
+the AlertDialog will always display the <code>dialog_text</code> that is located
+within <code>res/values/strings.xml</code>. </li>
+ </ul>
+
+ </li>
+</ol>
+
+<p>The project structure now looks like this:</p>
+
+ <img src="{@docRoot}images/hello_l10n/nonlocalized_project.png" alt="nonlocalized" width="394"
+height="320">
+
+<p class="note"><strong>Tip:</strong> If you will want to run the application on
+a device and not just on an emulator, open <code>AndroidManifest.xml</code> and
+add <code>android:debuggable="true"</code> inside the
+<code><application></code> element. For information about setting up the
+device itself so it can run applications from your system, see <a
+href="{@docRoot}guide/developing/device.html">Developing on a Device</a>.</p>
+
+
+<h2 id="run">Run the Unlocalized Application</h2>
+
+<p>Save the project and run the application to see how it works. No matter what
+locale your device or emulator is set to, the application runs the same way. It
+should look something like this:</p>
+
+<table border="0" cellspacing="0" cellpadding="30">
+ <tr>
+ <th scope="col">The unlocalized application, running in any locale:</th>
+ <th scope="col">After clicking the flag, in any locale:</th>
+ </tr>
+ <tr>
+ <td valign="top"><img src="{@docRoot}images/hello_l10n/nonlocalized_screenshot1.png"
+alt="nonlocalized" width="321" height="366"></td>
+ <td><img src="{@docRoot}images/hello_l10n/nonlocalized_screenshot2.png" alt="nonlocalized2"
+width="321" height="366"></td>
+ </tr>
+</table>
+<h2 id="plan">Plan the Localization</h2>
+<p>The first step in localizing an application is to plan how the application
+will render differently in different locales. In this application, the default
+locale will be the United Kingdom. We will add some locale-specific information
+for Germany, France, Canada, Japan, and the United States. Table 1 shows the
+plan for how the application will appear in different locales.</p>
+
+<p class="caption">Table 1</p>
+
+<table border="0" cellspacing="0" cellpadding="10">
+ <tr>
+ <th scope="col" valign="bottom">Region /<br />
+ Language</th>
+ <th scope="col">United Kingdom</th>
+ <th scope="col">Germany</th>
+ <th scope="col">France</th>
+ <th scope="col">Canada</th>
+ <th scope="col">Japan</th>
+ <th scope="col">United States</th>
+ <th scope="col">Other Location</th>
+ </tr>
+ <tr>
+ <th scope="row"><br>
+ English</th>
+ <td> British English text; British flag <em>(default)</em></td>
+ <td><em>-</em></td>
+ <td><em>-</em></td>
+ <td> British English text; Canadian flag</td>
+ <td>-</td>
+ <td> British English text; U.S. flag</td>
+ <td> British English text; British flag <em>(default)</em></td>
+ </tr>
+ <tr>
+ <th scope="row">German</th>
+ <td>-</td>
+ <td>German text for <code>app_name</code>, <code>text_a</code> and
+<code>text_b</code>; German flag</td>
+ <td>-</td>
+ <td>-</td>
+ <td>-</td>
+ <td>-</td>
+ <td>German text for <code>app_name</code>, <code>text_a</code> and
+<code>text_b</code>; British flag</td>
+ </tr>
+ <tr>
+ <th scope="row">French</th>
+ <td>-</td>
+ <td>-</td>
+ <td>French text for <code>app_name</code>, <code>text_a</code> and
+<code>text_b</code>; French flag</td>
+ <td>French text for <code>app_name</code>, <code>text_a</code> and
+<code>text_b</code>; Canadian flag</td>
+ <td>-</td>
+ <td>-</td>
+ <td>French text for <code>app_name</code>, <code>text_a</code> and
+<code>text_b</code>; British flag</td>
+ </tr>
+ <tr>
+ <th scope="row">Japanese</th>
+ <td>-</td>
+ <td>-</td>
+ <td>-</td>
+ <td>-</td>
+ <td>Japanese text for <code>text_a</code> and <code>text_b</code>; Japanese
+flag</td>
+ <td>-</td>
+ <td>Japanese text for <code>text_a</code> and <code>text_b</code>; British
+flag</td>
+ </tr>
+ <tr>
+ <th scope="row">Other Language</th>
+ <td>-</td>
+ <td>-</td>
+ <td>-</td>
+ <td>-</td>
+ <td>-</td>
+ <td>-</td>
+ <td> British English text; British flag <em>(default)</em></td>
+ </tr>
+</table>
+
+<p class="note"> Note that other behaviors are possible; for example, the
+application could support Canadian English or U.S. English text. But given the
+small amount of text involved, adding more versions of English would not make
+this application more useful.</p>
+
+<p>As shown in the table above, the plan calls for five flag icons in addition
+to the British flag that is already in the <code>res/drawable/</code> folder. It
+also calls for three sets of text strings other than the text that is in
+<code>res/values/strings.xml</code>.</p>
+
+<p>Table 2 shows where the needed text strings and flag icons will go, and
+specifies which ones will be loaded for which locales. (For more about the
+locale codes, <em></em>see <a
+href="{@docRoot}guide/topics/resources/resources-i18n.html#AlternateResources">
+Alternate Resources</a>.)</p>
+<p class="caption" id="table2">Table 2</p>
+
+<table border="1" cellspacing="0" cellpadding="5">
+ <tr>
+ <th scope="col">Locale Code</th>
+ <th scope="col">Language / Country</th>
+ <th scope="col">Location of strings.xml</th>
+ <th scope="col">Location of flag.png</th>
+ </tr>
+ <tr>
+ <td><em>Default</em></td>
+ <td>English / United Kingdom</td>
+ <td>res/values/</td>
+ <td>res/drawable/</td>
+ </tr>
+ <tr>
+ <td>de-rDE</td>
+ <td>German / Germany</td>
+ <td>res/values-de/</td>
+ <td>res/drawable-de-rDE/</td>
+ </tr>
+ <tr>
+ <td>fr-rFR</td>
+ <td>French / France</td>
+ <td>res/values-fr/</td>
+ <td>res/drawable-fr-rFR/</td>
+ </tr>
+ <tr>
+ <td>fr-rCA</td>
+ <td>French / Canada</td>
+ <td>res/values-fr/</td>
+ <td>res/drawable-fr-rCA/</td>
+ </tr>
+ <tr>
+ <td>en-rCA</td>
+ <td>English / Canada</td>
+ <td><em>(res/values/)</em></td>
+ <td>res/drawable-en-rCA/</td>
+ </tr>
+ <tr>
+ <td>ja-rJP</td>
+ <td>Japanese / Japan</td>
+ <td>res/values-ja/</td>
+ <td>res/drawable-ja-rJP/</td>
+ </tr>
+ <tr>
+ <td>en-rUS</td>
+ <td>English / United States</td>
+ <td><em>(res/values/)</em></td>
+ <td>res/drawable-en-rUS/</td>
+ </tr>
+</table>
+
+<p class="note"><strong>Tip: </strong>A folder qualifer cannot specify a region
+without a language. Having a folder named <code>res/drawable-rCA/</code>,
+for example, will prevent the application from compiling. </p>
+
+<p>At run time, the application will select a set of resources to load based on the locale
+that is set in the user's device. In cases where no locale-specific resources
+are available, the application will fall back on the defaults. </p>
+
+<p>For example, assume that the device's language is set to German and its
+location to Switzerland. Because this application does not have a
+<code>res/drawable-de-rCH/</code> directory with a <code>flag.png</code> file in it, the system
+will fall back on the default, which is the UK flag located in
+<code>res/drawable/flag.png</code>. The language used will be German. Showing a
+British flag to German speakers in Switzerland is not ideal, but for now we will
+just leave the behavior as it is. There are several ways you could improve this
+application's behavior if you wanted to:</p>
+
+<ul>
+ <li>Use a generic default icon. In this application, it might be something
+that represents Shakespeare. </li>
+ <li>Create a <code>res/drawable-de/</code> folder that includes an icon that
+the application will use whenever the language is set to German but the location
+is not Germany. </li>
+</ul>
+
+
+<h2 id="localize">Localize the Application</h2>
+
+<h3 id="localize_strings">Localize the Strings</h3>
+
+<p>The application requires three more <code>strings.xml</code> files, one
+each for German, French, and Japanese. To create these resource files within
+Eclipse:</p>
+
+<ol>
+<li>Select <strong>File</strong> > <strong>New</strong> > <strong>Android
+XML File</strong> to open the New Android XML File wizard. You can also open
+the wizard by clicking its icon in the toolbar:<br />
+<img src="{@docRoot}images/hello_l10n/xml_file_wizard_shortcut.png"
+alt="file_wizard_shortcut" width="297"
+height="90" style="margin:15px"></li>
+ <li>Select L10N for the Project field, and type <code>strings.xml</code> into
+the File field. In the left-hand list, select Language, then click the right arrow.<br>
+<img src="{@docRoot}images/hello_l10n/xml_wizard1.png" alt="res_file_copy" width="335"
+height="406" style="margin:15px"></li>
+ <li>Type <code>de</code> in the Language box and click Finish.<br>
+ <img src="{@docRoot}images/hello_l10n/xml_wizard2.png" alt="res_file_copy" width="306"
+height="179">
+<p>A new file, <code>res/values-de/strings.xml</code>, now appears among the project
+files.</p></li>
+<li>Repeat the steps twice more, for the language codes <code>fr</code> and
+ <code>ja</code>.
+Now the project includes these new skeleton files: <br />
+ <code>res/<strong>values-de</strong>/strings.xml</code><br />
+ <code>res/<strong>values-fr</strong>/strings.xml</code><br />
+ <code>res/<strong>values-ja</strong>/strings.xml</code><br />
+ </li>
+ <li>Add localized text to the new files. To do
+this, open the <code>res/values-<em><qualifier></em>/strings.xml</code> files and
+replace the code as follows:</li>
+</ol>
+
+<table border="0" cellspacing="0" cellpadding="0">
+ <tr>
+ <th scope="col">File</th>
+ <th scope="col">Replace the contents with the following code:</th>
+ </tr>
+ <tr>
+ <td><code>res/values-de/strings.xml</code></td>
+ <td><pre><?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">Hallo, Lokalisierung</string>
+ <string name="text_a">Soll ich dich einem Sommertag vergleichen,</string>
+ <string name="text_b">Der du viel lieblicher und sanfter bist?</string>
+</resources></pre></td>
+ </tr>
+ <tr>
+ <td><code>res/values-fr/strings.xml</code></td>
+ <td><pre><?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">Bonjour, Localisation</string>
+ <string name="text_a">Irai-je te comparer au jour d'été?</string>
+ <string name="text_b">Tu es plus tendre et bien plus tempéré.</string>
+</resources> </pre></td>
+ </tr>
+ <tr>
+ <td><code>res/values-ja/strings.xml</code></td>
+ <td>
+<pre><?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="text_a">あなたをなにかにたとえるとしたら夏の一日でしょうか?</string>
+ <string name="text_b">だがあなたはもっと美しく、もっとおだやかです。</string>
+</resources></pre></td>
+ </tr>
+</table>
+
+<p class="note"><b>Tip:</b> In the
+<code>values-<em><qualifier></em>/strings.xml</code> files, you only need to
+include text for strings that are different from the default strings. For
+example, when the application runs on a device that is configured for Japanese,
+the plan is for <code>text_a</code> and <code>text_b</code> to be in Japanese
+while all the other text is in English, so
+<code>res/values-ja/strings.xml</code> only needs to include <code>text_a</code>
+and <code>text_b</code>.</p>
+
+<h3 id="localize_images">Localize the Images</h3>
+
+<p>As shown in <a href="#table2">Table 2</a>, the application needs six more
+drawable folders, each containing a <code>flag.png</code> icon. Add the needed
+icons and folders to your project:</p>
+
+<ol>
+ <li>Save this <a href="../../../images/hello_l10n/drawable-de-rDE/flag.png">German flag icon</a>
+as <code>res/drawable-de-rDE/flag.png</code> in the application's project
+workspace.
+ <p>For example:</p>
+ <ol>
+ <li>Click the link to open the flag image.</li>
+ <li>Save the image in
+<code><em>your-workspace</em>/HelloL10N/res/drawable-de-rDE/</code> .</li>
+ </ol>
+ </li>
+ <li>Save this <a href="../../../images/hello_l10n/drawable-fr-rFR/flag.png">French flag icon</a>
+as <code>res/drawable-fr-rFR/flag.png</code> in the application's project
+workspace. </li>
+ <li>Save this <a href="../../../images/hello_l10n/drawable-fr-rCA/flag.png">Canadian flag icon</a>
+as <code>res/drawable-fr-rCA/flag.png</code> in the project workspace. </li>
+ <li>Save the <a href="../../../images/hello_l10n/drawable-en-rCA/flag.png">Canadian flag icon</a>
+again, this time as <code>res/drawable-en-rCA/flag.png</code> in the project
+workspace. (Why not have just <em>one</em> folder that contains the Canadian
+flag? Because a folder qualifer cannot specify a region without a language.
+You cannot have a folder named <code>drawable-rCA/</code>; instead you must
+create two separate folders, one for each of the Canadian languages represented
+in the application.)</li>
+ <li>Save this <a href="../../../images/hello_l10n/drawable-ja-rJP/flag.png">Japanese flag icon</a>
+as <code>res/drawable-ja-rJP/flag.png</code> in the project workspace. </li>
+ <li>Save this <a href="../../../images/hello_l10n/drawable-en-rUS/flag.png">United States flag
+icon</a> as <code>res/drawable-en-rUS/flag.png</code> in the project workspace.
+ </li>
+</ol>
+
+<p>If you are using Eclipse, refresh the project (F5). The new
+<code>res/drawable-<em><qualifier></em>/</code> folders should appear in the
+project view. </p>
+
+
+<h2 id="test_localized">Run and Test the Localized Application</h2>
+
+<p>Once you've added the localized string and image resources, you are ready to
+ run the application and test its handling of them. To change the locale
+ on a device or in the emulator, use the Settings
+application (Home > Menu > Settings > Locale & text > Select
+locale). Depending on how a device was configured, it might not offer any
+alternate locales via the Settings application, or might offer only a few. The
+emulator, on the other hand, will offer a selection of all the locales that are
+available in the Android system image. </p>
+
+<p>To set the emulator to a locale that is not available in the system image,
+use the Custom Locale application, which is available in the Application
+tab:</p>
+
+<p><img src="{@docRoot}images/hello_l10n/custom_locale_app.png" alt="custom locale app" width="163"
+height="158" style="margin-left:15px"></p>
+
+<p>To switch to a new locale, long-press a locale name:</p>
+
+<p><img src="{@docRoot}images/hello_l10n/using_custom_locale.png" alt="using custom locale"
+width="512" height="299" style="margin-left:15px"></p>
+
+<p>For a list of locales available on different versions of the Android platform,
+refer to the platform notes documents, listed under "Downloadable SDK Components"
+in the "SDK" tab. For example, <a
+href="{@docRoot}sdk/android-2.0.html#locs">Android 2.0 locales</a>.</p>
+
+<p>Run the application for each of the expected locales, plus one unexpected
+locale. Here are some of the results you should see:</p>
+
+<table border="0" cellspacing="0" cellpadding="05">
+ <tr>
+ <th scope="col">Locale</th>
+ <th scope="col">Opening screen of application</th>
+ </tr>
+ <tr>
+ <td>German / Germany
+ <br />Specifically supported by the Hello, L10N application.</td>
+ <td><img src="{@docRoot}images/hello_l10n/german_screenshot.png" alt="custom locale app"
+width="321" height="175" align="right"
+style="margin-left:10px;margin-right:20px"></td>
+ </tr>
+ <tr>
+ <td>French / Canada
+ <br />Specifically supported by the Hello, L10N application.</td>
+ <td><img src="{@docRoot}images/hello_l10n/frenchCA_screenshot.png" alt="custom locale app"
+width="321" height="175" align="right"
+style="margin-left:10px;margin-right:20px"></td>
+ </tr>
+ <tr>
+ <td>German / Switzerland
+ <br />Only the language is specifically supported by
+the Hello, L10N application.</td>
+ <td><img src="{@docRoot}images/hello_l10n/germanCH_screenshot.png" alt="custom locale app"
+width="321" height="175" align="right"
+style="margin-left:10px;margin-right:20px">`</td>
+ </tr>
+ <tr>
+ <td>Japanese
+ <br />Specifically supported by the Hello, L10N application.
+ </td>
+ <td><img src="{@docRoot}images/hello_l10n/japanese_screenshot.png" alt="custom locale app"
+width="321" height="220" align="right"
+style="margin-left:10px;margin-right:20px">`</td>
+ </tr>
+ <tr>
+ <td>Romansh / Switzerland (custom locale <code>rm_CH</code>)
+ <br />Not specifically supported by the Hello, L10N
+application, so the application uses the default resources.</td>
+ <td><img src="{@docRoot}images/hello_l10n/romanshCH_screenshot.png" alt="custom locale app"
+width="321" height="175" align="right"
+style="margin-left:10px;margin-right:20px"></td>
+ </tr>
+</table>
diff --git a/docs/html/resources/tutorials/notepad/codelab/NotepadCodeLab.zip b/docs/html/resources/tutorials/notepad/codelab/NotepadCodeLab.zip
new file mode 100644
index 0000000..24fefc1
--- /dev/null
+++ b/docs/html/resources/tutorials/notepad/codelab/NotepadCodeLab.zip
Binary files differ
diff --git a/docs/html/resources/tutorials/notepad/index.jd b/docs/html/resources/tutorials/notepad/index.jd
new file mode 100644
index 0000000..f569314
--- /dev/null
+++ b/docs/html/resources/tutorials/notepad/index.jd
@@ -0,0 +1,142 @@
+page.title=Notepad Tutorial
+@jd:body
+
+
+<p>This tutorial on writing a notepad application gives you a "hands-on" introduction
+to the Android framework and the tools you use to build applications on it.
+Starting from a preconfigured project file, it guides you through the process of
+developing a simple notepad application and provides concrete examples of how to
+set up the project, develop the application logic and user interface, and then
+compile and run the application. </p>
+
+<p>The tutorial presents the application development as a set of
+exercises (see below), each consisting of several steps. You should follow
+the steps in each exercise to gradually build and refine your
+application. The exercises explain each step in detail and provide all the
+sample code you need to complete the application. </p>
+
+<p>When you are finished with the tutorial, you will have created a functioning
+Android application and will have learned many of the most important
+concepts in Android development. If you want to add more complex features to
+your application, you can examine the code in an alternative implementation
+of a Note Pad application, in the
+<a href="{@docRoot}resources/samples/index.html">Sample Code</a> section. </p>
+
+
+<a name="who"></a>
+<h2>Who Should Use this Tutorial</h2>
+
+<p>This tutorial is designed for experienced developers, especially those with
+knowledge of the Java programming language. If you haven't written Java
+applications before, you can still use the tutorial, but you might need to work
+at a slower pace. </p>
+
+<p>Also note that this tutorial uses
+the Eclipse development environment, with the Android plugin installed. If you
+are not using Eclipse, you can follow the exercises and build the application,
+but you will need to determine how to accomplish the Eclipse-specific
+steps in your environment. </p>
+
+<a name="preparing"></a>
+<h2>Preparing for the Exercises</h2>
+
+<p>The tutorial assumes that you have some familiarity with basic Android
+application concepts and terminology. If you are not, you
+should read <a href="{@docRoot}guide/topics/fundamentals.html">Application
+Fundamentals</a> before continuing. </p>
+
+<p>This tutorial also builds on the introductory information provided in the
+<a href="{@docRoot}resources/tutorials/hello-world.html">Hello World</a>
+tutorial, which explains how to set up your Eclipse environment
+for building Android applications. We recommend you complete the Hello World
+tutorial before starting this one.</p>
+
+<p>To prepare for this lesson:</p>
+
+<ol>
+ <li>Download the <a href="codelab/NotepadCodeLab.zip">project
+ exercises archive (.zip)</a>.</li>
+ <li>Unpack the archive file to a suitable location on your machine.</li>
+ <li>Open the <code>NotepadCodeLab</code> folder.</li>
+</ol>
+
+<p>Inside the <code>NotepadCodeLab</code> folder, you should see six project
+files: <code>Notepadv1</code>,
+ <code>Notepadv2</code>, <code>Notepadv3</code>,
+ <code>Notepadv1Solution</code>, <code>Notepadv2Solution</code>
+ and <code>Notepadv3Solution</code>. The <code>Notepadv#</code> projects are
+the starting points for each of the exercises, while the
+<code>Notepadv#Solution</code> projects are the exercise
+ solutions. If you are having trouble with a particular exercise, you
+ can compare your current work against the exercise solution.</p>
+
+<a name="exercises"></a>
+<h2> Exercises</h2>
+
+ <p>The table below lists the tutorial exercises and describes the development
+areas that each covers. Each exercise assumes that you have completed any
+previous exercises.</p>
+
+ <table border="0" style="padding:4px;spacing:2px;" summary="This
+table lists the
+tutorial examples and describes what each covers. ">
+ <tr>
+ <th width="120"><a href="{@docRoot}resources/tutorials/notepad/notepad-ex1.html">Exercise
+1</a></th>
+ <td>Start here. Construct a simple notes list that lets the user add new notes but not
+edit them. Demonstrates the basics of <code>ListActivity</code> and creating
+and handling
+ menu options. Uses a SQLite database to store the notes.</td>
+ </tr>
+ <tr>
+ <th><a href="{@docRoot}resources/tutorials/notepad/notepad-ex2.html">Exercise 2</a></th>
+ <td>Add a second Activity to the
+application. Demonstrates constructing a
+new Activity, adding it to the Android manifest, passing data between the
+activities, and using more advanced screen layout. Also shows how to
+invoke another Activity to return a result, using
+<code>startActivityForResult()</code>.</td>
+ </tr>
+ <tr>
+ <th><a href="{@docRoot}resources/tutorials/notepad/notepad-ex3.html">Exercise 3</a></th>
+ <td>Add handling of life-cycle events to
+the application, to let it
+maintain application state across the life cycle. </td>
+ </tr>
+ <tr>
+ <th><a href="{@docRoot}resources/tutorials/notepad/notepad-extra-credit.html">Extra
+Credit</a></th>
+ <td>Demonstrates how to use the Eclipse
+debugger and how you can use it to
+view life-cycle events as they are generated. This section is optional but
+highly recommended.</td>
+ </tr>
+</table>
+
+
+<a name="other"></a>
+<h2>Other Resources and Further Learning</h2>
+<ul>
+<li>For a lighter but broader introduction to concepts not covered in the
+tutorial,
+take a look at <a href="{@docRoot}resources/faq/commontasks.html">Common Android Tasks</a>.</li>
+<li>The Android SDK includes a variety of fully functioning sample applications
+that make excellent opportunities for further learning. You can find the sample
+applications in the <code>samples/</code> directory of your downloaded SDK, or browser them
+here, in the <a href="{@docRoot}resources/samples/index.html">Sample Code</a> section.</li>
+<li>This tutorial draws from the full Notepad application included in the
+<code>samples/</code> directory of the SDK, though it does not match it exactly.
+When you are done with the tutorial,
+it is highly recommended that you take a closer look at this version of the Notepad
+application,
+as it demonstrates a variety of interesting additions for your application,
+such as:</li>
+ <ul>
+ <li>Setting up a custom striped list for the list of notes.</li>
+ <li>Creating a custom text edit view that overrides the <code>draw()</code>
+ method to make it look like a lined notepad.</li>
+ <li>Implementing a full <code>ContentProvider</code> for notes.</li>
+ <li>Reverting and discarding edits instead of just automatically saving
+ them.</li>
+ </ul>
+</ul>
diff --git a/docs/html/resources/tutorials/notepad/notepad-ex1.jd b/docs/html/resources/tutorials/notepad/notepad-ex1.jd
new file mode 100644
index 0000000..b26440b
--- /dev/null
+++ b/docs/html/resources/tutorials/notepad/notepad-ex1.jd
@@ -0,0 +1,591 @@
+page.title=Notepad Exercise 1
+parent.title=Notepad Tutorial
+parent.link=index.html
+@jd:body
+
+
+<p><em>In this exercise, you will construct a simple notes list that lets the
+user add new notes but not edit them. The exercise demonstrates:</em></p>
+<ul>
+<li><em>The basics of <code>ListActivities</code> and creating and handling menu
+options. </em></li>
+<li><em>How to use a SQLite database to store the notes.</em></li>
+<li><em>How to bind data from a database cursor into a ListView using a
+SimpleCursorAdapter.</em></li>
+<li><em>The basics of screen layouts, including how to lay out a list view, how
+you can add items to the activity menu, and how the activity handles those menu
+selections. </em></li>
+</ul>
+
+<div style="float:right;white-space:nowrap">
+<span style="color:#BBB;">
+ [<a href="notepad-ex1.html" style="color:#BBB;">Exercise 1</a>]</span>
+ [<a href="notepad-ex2.html">Exercise 2</a>]
+ [<a href="notepad-ex3.html">Exercise 3</a>]
+ [<a href="notepad-extra-credit.html">Extra Credit</a>]
+</div>
+
+
+
+<h2>Step 1</h2>
+
+ <p>Open up the <code>Notepadv1</code> project in Eclipse.</p>
+
+ <p><code>Notepadv1</code> is a project that is provided as a starting point. It
+ takes care of some of the boilerplate work that you have already seen if you
+ followed the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello,
+ World</a> tutorial.</p>
+
+ <ol>
+ <li>
+ Start a new Android Project by clicking <strong>File</strong> >
+ <strong>New</strong> > <strong>Android Project</strong>.</li>
+ <li>
+ In the New Android Project dialog, select <strong>Create project from existing source</strong>.</li>
+ <li>
+ Click <strong>Browse</strong> and navigate to where you copied the <code>NotepadCodeLab</code>
+ (downloaded during <a href="{@docRoot}resources/tutorials/notepad/index.html#preparing">setup</a>)
+ and select <code>Notepadv1</code>.</li>
+ <li>
+ The Project Name and other properties should be automatically filled for you.
+ You must select the Build Target—we recommend selecting a target with the
+ lowest platform version available. Also add an integer to the Min SDK Version field
+ that matches the API Level of the selected Build Target.</li>
+ <li>
+ Click <strong>Finish</strong>. The <code>Notepadv1</code> project should open and be
+ visible in your Eclipse package explorer.</li>
+ </ol>
+
+ <p>If you see an error about <code>AndroidManifest.xml</code>, or some
+ problems related to an Android zip file, right click on the project and
+ select <strong>Android Tools</strong> > <strong>Fix Project Properties</strong>.
+ (The project is looking in the wrong location for the library file,
+ this will fix it for you.)</p>
+
+ <h2>Step 2</h2>
+
+ <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
+ background-color:#FFFFEE;margin-right:0px;
+ margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
+ <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
+ background-color:#FFFFDD;">Accessing and modifying data</h2>
+ <p style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">For this
+ exercise, we are using a SQLite database to store our data. This is useful
+ if only <em>your</em> application will need to access or modify the data. If you wish for
+ other activities to access or modify the data, you have to expose the data using a
+ {@link android.content.ContentProvider ContentProvider}.</p>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">If you are interested, you can find out more about
+ <a href="{@docRoot}guide/topics/providers/content-providers.html">content providers</a> or the whole
+ subject of <a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a>.
+ The NotePad sample in the <code>samples/</code> folder of the SDK also has an example of how
+ to create a ContentProvider.</p>
+ </div>
+
+ <p>Take a look at the <code>NotesDbAdapter</code> class — this class is provided to
+ encapsulate data access to a SQLite database that will hold our notes data
+ and allow us to update it.</p>
+ <p>At the top of the class are some constant definitions that will be used in the application
+ to look up data from the proper field names in the database. There is also a database creation
+ string defined, which is used to create a new database schema if one doesn't exist already.</p>
+ <p>Our database will have the name <code>data</code>, and have a single table called
+ <code>notes</code>, which in turn has three fields: <code>_id</code>, <code>title</code> and
+ <code>body</code>. The <code>_id</code> is named with an underscore convention used in a number of
+ places inside the Android SDK and helps keep a track of state. The <code>_id</code>
+ usually has to be specified when querying or updating the database (in the column projections
+ and so on). The other two fields are simple text fields that will store data.
+ </p>
+ <p>The constructor for <code>NotesDbAdapter</code> takes a Context, which allows it to communicate with aspects
+ of the Android operating system. This is quite common for classes that need to touch the
+ Android system in some way. The Activity class implements the Context class, so usually you will just pass
+ <code>this</code> from your Activity, when needing a Context.</p>
+ <p>The <code>open()</code> method calls up an instance of DatabaseHelper, which is our local
+ implementation of the SQLiteOpenHelper class. It calls <code>getWritableDatabase()</code>,
+ which handles creating/opening a database for us.</p>
+ <p><code>close()</code> just closes the database, releasing resources related to the
+ connection.</p>
+ <p><code>createNote()</code> takes strings for the title and body of a new note,
+ then creates that note in the database. Assuming the new note is created successfully, the
+ method also returns the row <code>_id</code> value for the newly created note.</p>
+ <p><code>deleteNote()</code> takes a <var>rowId</var> for a particular note, and deletes that note from
+ the database.</p>
+
+ <p><code>fetchAllNotes()</code> issues a query to return a {@link android.database.Cursor} over all notes in the
+ database. The <code>query()</code> call is worth examination and understanding. The first field is the
+ name of the database table to query (in this case <code>DATABASE_TABLE</code> is "notes").
+ The next is the list of columns we want returned, in this case we want the <code>_id</code>,
+ <code>title</code> and <code>body</code> columns so these are specified in the String array.
+ The remaining fields are, in order: <code>selection</code>,
+ <code>selectionArgs</code>, <code>groupBy</code>, <code>having</code> and <code>orderBy</code>.
+ Having these all <code>null</code> means we want all data, need no grouping, and will take the default
+ order. See {@link android.database.sqlite.SQLiteDatabase SQLiteDatabase} for more details.</p>
+ <p class="note"><b>Note:</b> A Cursor is returned rather than a collection of rows. This allows
+ Android to use resources efficiently -- instead of putting lots of data straight into memory
+ the cursor will retrieve and release data as it is needed, which is much more efficient for
+ tables with lots of rows.</p>
+
+ <p><code>fetchNote()</code> is similar to <code>fetchAllNotes()</code> but just gets one note
+ with the <var>rowId</var> we specify. It uses a slightly different version of the
+ {@link android.database.sqlite.SQLiteDatabase} <code>query()</code> method.
+ The first parameter (set <em>true</em>) indicates that we are interested
+ in one distinct result. The <var>selection</var> parameter (the fourth parameter) has been specified to search
+ only for the row "where _id =" the <var>rowId</var> we passed in. So we are returned a Cursor on
+ the one row.</p>
+ <p>And finally, <code>updateNote()</code> takes a <var>rowId</var>, <var>title</var> and <var>body</var>, and uses a
+ {@link android.content.ContentValues ContentValues} instance to update the note of the given
+ <var>rowId</var>.</p>
+
+<h2 style="clear:right;">Step 3</h2>
+
+ <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
+ background-color:#FFFFEE;margin-right:0px;
+ margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
+ <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
+ background-color:#FFFFDD;">Layouts and activities</h2>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">Most Activity classes will have a layout associated with them. The layout
+ will be the "face" of the Activity to the user. In this case our layout will
+ take over the whole screen and provide a list of notes.</p>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">Full screen layouts are not the only option for an Activity however. You
+ might also want to use a <a
+href="{@docRoot}resources/faq/commontasks.html#floatingorfull">floating
+ layout</a> (for example, a <a
+href="{@docRoot}resources/faq/commontasks.html#dialogsandalerts">dialog
+ or alert</a>),
+ or perhaps you don't need a layout at all (the Activity will be invisible
+ to the user unless you specify some kind of layout for it to use).</p>
+ </div>
+
+ <p>Open the <code>notepad_list.xml</code> file in <code>res/layout</code>
+and
+ take a look at it. (You may have to
+ hit the <em>xml</em> tab, at the bottom, in order to view the XML markup.)</p>
+
+ <p>This is a mostly-empty layout definition file. Here are some
+ things you should know about a layout file:</p>
+
+
+ <ul>
+ <li>
+ All Android layout files must start with the XML header line:
+ <code><?xml version="1.0" encoding="utf-8"?></code>. </li>
+ <li>
+ The next definition will often (but not always) be a layout
+ definition of some kind, in this case a <code>LinearLayout</code>. </li>
+ <li>
+ The XML namespace of Android should always be defined in
+ the top level component or layout in the XML so that <code>android:</code> tags can
+ be used through the rest of the file:
+ <p><code>xmlns:android="http://schemas.android.com/apk/res/android"</code></p>
+ </li>
+ </ul>
+
+ <h2 style="clear:right;">Step 4</h2>
+ <p>We need to create the layout to hold our list. Add code inside
+ of the <code>LinearLayout</code> element so the whole file looks like this: </p>
+ <pre>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ListView android:id="@android:id/list"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <TextView android:id="@android:id/empty"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/no_notes"/>
+
+</LinearLayout>
+</pre>
+ <ul>
+ <li>
+ The <strong>@</strong> symbol in the id strings of the <code>ListView</code> and
+ <code>TextView</code> tags means
+ that the XML parser should parse and expand the rest of
+ the id string and use an ID resource.</li>
+ <li>
+ The <code>ListView</code> and <code>TextView</code> can be
+ thought as two alternative views, only one of which will be displayed at once.
+ ListView will be used when there are notes to be shown, while the TextView
+ (which has a default value of "No Notes Yet!" defined as a string
+ resource in <code>res/values/strings.xml</code>) will be displayed if there
+ aren't any notes to display.</li>
+ <li>The <code>list</code> and <code>empty</code> IDs are
+ provided for us by the Android platform, so, we must
+ prefix the <code>id</code> with <code>android:</code> (e.g., <code>@android:id/list</code>).</li>
+ <li>The View with the <code>empty</code> id is used
+ automatically when the {@link android.widget.ListAdapter} has no data for the ListView. The
+ ListAdapter knows to look for this name by default. Alternatively, you could change the
+ default empty view by using {@link android.widget.AdapterView#setEmptyView(View)}
+ on the ListView.
+ <p>
+ More broadly, the <code>android.R</code> class is a set of predefined
+ resources provided for you by the platform, while your project's
+ <code>R</code> class is the set of resources your project has defined.
+ Resources found in the <code>android.R</code> resource class can be
+ used in the XML files by using the <code>android:</code> name space prefix
+ (as we see here).</p>
+ </li>
+ </ul>
+
+ <h2 style="clear:right;">Step 5</h2>
+
+ <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
+ background-color:#FFFFEE;margin-right:0px;
+ margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
+ <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
+ background-color:#FFFFDD;">Resources and the R class</h2>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">The folders under res/ in the Eclipse project are for resources.
+ There is a <a href="{@docRoot}resources/faq/commontasks.html#filelist">specific structure</a> to the
+ folders and files under res/.</p>
+ <p style="padding-left:.5em;font-size:12px;
+margin:0; padding:.0em .5em .5em 1em;">Resources defined in these folders and files will have
+ corresponding entries in the R class allowing them to be easily accessed
+ and used from your application. The R class is automatically generated using the contents
+ of the res/ folder by the eclipse plugin (or by aapt if you use the command line tools).
+ Furthermore, they will be bundled and deployed for you as part of the application.</p>
+ </p>
+ </div>
+ <p>To make the list of notes in the ListView, we also need to define a View for each row:</p>
+ <ol>
+ <li>
+ Create a new file under <code>res/layout</code> called
+ <code>notes_row.xml</code>. </li>
+ <li>
+ Add the following contents (note: again the XML header is used, and the
+ first node defines the Android XML namespace)<br>
+ <pre style="overflow:auto">
+<?xml version="1.0" encoding="utf-8"?>
+<TextView android:id="@+id/text1"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/></pre>
+ <p>
+ This is the View that will be used for each notes title row — it has only
+ one text field in it. </p>
+ <p>In this case we create a new id called <code>text1</code>. The
+ <strong>+</strong> after the <strong>@</strong> in the id string indicates that the id should
+ be automatically created as a resource if it does not already exist, so we are defining
+ <code>text1</code> on the fly and then using it.</p>
+ </li>
+ <li>Save the file.</li>
+ </ol>
+ <p>Open the <code>R.java</code> class in the
+ project and look at it, you should see new definitions for
+ <code>notes_row</code> and <code>text1</code> (our new definitions)
+ meaning we can now gain access to these from the our code. </p>
+
+ <h2 style="clear:right;">Step 6</h2>
+<p>Next, open the <code>Notepadv1</code> class in the source. In the following steps, we are going to
+ alter this class to become a list adapter and display our notes, and also
+ allow us to add new notes.</p>
+
+<p><code>Notepadv1</code> will inherit from a subclass
+ of <code>Activity</code> called a <code>ListActivity</code>,
+ which has extra functionality to accommodate the kinds of
+ things you might want to do with a list, for
+ example: displaying an arbitrary number of list items in rows on the screen,
+ moving through the list items, and allowing them to be selected.</p>
+
+<p>Take a look through the existing code in <code>Notepadv1</code> class.
+ There is a currently an unused private field called <code>mNoteNumber</code> that
+ we will use to create numbered note titles.</p>
+ <p>There are also three override methods defined:
+ <code>onCreate</code>, <code>onCreateOptionsMenu</code> and
+ <code>onOptionsItemSelected</code>; we need to fill these
+ out:</p>
+ <ul>
+ <li><code>onCreate()</code> is called when the activity is
+ started — it is a little like the "main" method for an Activity. We use
+ this to set up resources and state for the activity when it is
+ running.</li>
+ <li><code>onCreateOptionsMenu()</code> is used to populate the
+ menu for the Activity. This is shown when the user hits the menu button,
+and
+ has a list of options they can select (like "Create
+ Note"). </li>
+ <li><code>onOptionsItemSelected()</code> is the other half of the
+ menu equation, it is used to handle events generated from the menu (e.g.,
+ when the user selects the "Create Note" item).
+ </li>
+ </ul>
+
+ <h2>Step 7</h2>
+ <p>Change the inheritance of <code>Notepadv1</code> from
+<code>Activity</code>
+ to <code>ListActivity</code>:</p>
+ <pre>public class Notepadv1 extends ListActivity</pre>
+ <p>Note: you will have to import <code>ListActivity</code> into the
+Notepadv1
+ class using Eclipse, <strong>ctrl-shift-O</strong> on Windows or Linux, or
+ <strong>cmd-shift-O</strong> on the Mac (organize imports) will do this for you
+ after you've written the above change.</p>
+
+ <h2>Step 8</h2>
+ <p>Fill out the body of the <code>onCreate()</code> method.</p>
+ <p>Here we will set the title for the Activity (shown at the top of the
+ screen), use the <code>notepad_list</code> layout we created in XML,
+ set up the <code>NotesDbAdapter</code> instance that will
+ access notes data, and populate the list with the available note
+ titles:</p>
+ <ol>
+ <li>
+ In the <code>onCreate</code> method, call <code>super.onCreate()</code> with the
+ <code>savedInstanceState</code> parameter that's passed in.</li>
+ <li>
+ Call <code>setContentView()</code> and pass <code>R.layout.notepad_list</code>.</li>
+ <li>
+ At the top of the class, create a new private class field called <code>mDbHelper</code> of class
+ <code>NotesDbAdapter</code>.
+ </li>
+ <li>
+ Back in the <code>onCreate</code> method, construct a new
+<code>NotesDbAdapter</code>
+ instance and assign it to the <code>mDbHelper</code> field (pass
+ <code>this</code> into the constructor for <code>DBHelper</code>)
+ </li>
+ <li>
+ Call the <code>open()</code> method on <code>mDbHelper</code> to open (or create) the
+ database.
+ </li>
+ <li>
+ Finally, call a new method <code>fillData()</code>, which will get the data and
+ populate the ListView using the helper — we haven't defined this method yet. </li>
+ </ol>
+ <p>
+ <code>onCreate()</code> should now look like this:</p>
+ <pre>
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.notepad_list);
+ mDbHelper = new NotesDbAdapter(this);
+ mDbHelper.open();
+ fillData();
+ }</pre>
+ <p>And be sure you have the <code>mDbHelper</code> field definition (right
+ under the mNoteNumber definition): </p>
+ <pre> private NotesDbAdapter mDbHelper;</pre>
+
+ <h2>Step 9</h2>
+
+ <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
+ background-color:#FFFFEE;margin-right:0px;
+ margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
+ <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
+ background-color:#FFFFDD;">More on menus</h2>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">The notepad application we are constructing only scratches the
+ surface with <a href="{@docRoot}resources/faq/commontasks.html#addmenuitems">menus</a>. </p>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">You can also <a href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">add
+shortcut keys for menu items</a>, <a href="{@docRoot}resources/faq/commontasks.html#menukeyshortcuts">create
+submenus</a> and even <a href="{@docRoot}resources/faq/commontasks.html#addingtoothermenus">add
+menu items to other applications!</a>. </p>
+ </div>
+
+<p>Fill out the body of the <code>onCreateOptionsMenu()</code> method.</p>
+
+<p>We will now create the "Add Item" button that can be accessed by pressing the menu
+button on the device. We'll specify that it occupy the first position in the menu.</p>
+
+ <ol>
+ <li>
+ In <code>strings.xml</code> resource (under <code>res/values</code>), add
+ a new string named "menu_insert" with its value set to <code>Add Item</code>:
+ <pre><string name="menu_insert">Add Item</string></pre>
+ <p>Then save the file and return to <code>Notepadv1</code>.</p>
+ </li>
+ <li>Create a menu position constant at the top of the class:
+ <pre>public static final int INSERT_ID = Menu.FIRST;</pre>
+ </li>
+ <li>In the <code>onCreateOptionsMenu()</code> method, change the
+ <code>super</code> call so we capture the boolean return as <code>result</code>. We'll return this value at the end.</li>
+ <li>Then add the menu item with <code>menu.add()</code>.</li>
+ </ol>
+ <p>The whole method should now look like this:
+ <pre>
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ boolean result = super.onCreateOptionsMenu(menu);
+ menu.add(0, INSERT_ID, 0, R.string.menu_insert);
+ return result;
+ }</pre>
+ <p>The arguments passed to <code>add()</code> indicate: a group identifier for this menu (none,
+ in this case), a unique ID (defined above), the order of the item (zero indicates no preference),
+ and the resource of the string to use for the item.</p>
+
+<h2 style="clear:right;">Step 10</h2>
+ <p>Fill out the body of the <code>onOptionsItemSelected()</code> method:</p>
+ <p>This is going
+ to handle our new "Add Note" menu item. When this is selected, the
+ <code>onOptionsItemSelected()</code> method will be called with the
+ <code>item.getId()</code> set to <code>INSERT_ID</code> (the constant we
+ used to identify the menu item). We can detect this, and take the
+ appropriate actions:</p>
+ <ol>
+ <li>
+ The <code>super.onOptionsItemSelected(item)</code> method call goes at the
+ end of this method — we want to catch our events first! </li>
+ <li>
+ Write a switch statement on <code>item.getItemId()</code>.
+ <p>In the case of <var>INSERT_ID</var>, call a new method, <code>createNote()</code>,
+ and return true, because we have handled this event and do not want to
+ propagate it through the system.</p>
+ </li>
+ <li>Return the result of the superclass' <code>onOptionsItemSelected()</code>
+ method at the end.</li>
+ </ol>
+ <p>
+ The whole <code>onOptionsItemSelect()</code> method should now look like
+ this:</p>
+ <pre>
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case INSERT_ID:
+ createNote();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }</pre>
+
+<h2>Step 11</h2>
+ <p>Add a new <code>createNote()</code> method:</p>
+ <p>In this first version of
+ our application, <code>createNote()</code> is not going to be very useful.
+We will simply
+ create a new note with a title assigned to it based on a counter ("Note 1",
+ "Note 2"...) and with an empty body. At present we have no way of editing
+ the contents of a note, so for now we will have to be content making one
+ with some default values:</p>
+ <ol>
+ <li>Construct the name using "Note" and the counter we defined in the class: <code>
+ String noteName = "Note " + mNoteNumber++</code></li>
+ <li>
+ Call <code>mDbHelper.createNote()</code> using <code>noteName</code> as the
+ title and <code>""</code> for the body
+ </li>
+ <li>
+ Call <code>fillData()</code> to populate the list of notes (inefficient but
+ simple) — we'll create this method next.</li>
+ </ol>
+ <p>
+ The whole <code>createNote()</code> method should look like this: </p>
+ <pre>
+ private void createNote() {
+ String noteName = "Note " + mNoteNumber++;
+ mDbHelper.createNote(noteName, "");
+ fillData();
+ }</pre>
+
+
+<h2>Step 12</h2>
+ <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
+ background-color:#FFFFEE;margin-right:0px;
+ margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
+ <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
+ background-color:#FFFFDD;">List adapters</h2>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">Our example uses a {@link android.widget.SimpleCursorAdapter
+ SimpleCursorAdapter} to bind a database {@link android.database.Cursor Cursor}
+ into a ListView, and this is a common way to use a {@link android.widget.ListAdapter
+ ListAdapter}. Other options exist like {@link android.widget.ArrayAdapter ArrayAdapter} which
+ can be used to take a List or Array of in-memory data and bind it in to
+ a list as well.</p>
+ </div>
+
+ <p>Define the <code>fillData()</code> method:</p>
+ <p>This
+ method uses <code>SimpleCursorAdapter,</code> which takes a database <code>Cursor</code>
+ and binds it to fields provided in the layout. These fields define the row elements of our list
+ (in this case we use the <code>text1</code> field in our
+ <code>notes_row.xml</code> layout), so this allows us to easily populate the list with
+ entries from our database.</p>
+ <p>To do this we have to provide a mapping from the <code>title</code> field in the returned Cursor, to
+ our <code>text1</code> TextView, which is done by defining two arrays: the first a string array
+ with the list of columns to map <em>from</em> (just "title" in this case, from the constant
+ <code>NotesDbAdapter.KEY_TITLE</code>) and, the second, an int array
+ containing references to the views that we'll bind the data <em>into</em>
+ (the <code>R.id.text1</code> TextView).</p>
+ <p>This is a bigger chunk of code, so let's first take a look at it:</p>
+
+ <pre>
+ private void fillData() {
+ // Get all of the notes from the database and create the item list
+ Cursor c = mDbHelper.fetchAllNotes();
+ startManagingCursor(c);
+
+ String[] from = new String[] { NotesDbAdapter.KEY_TITLE };
+ int[] to = new int[] { R.id.text1 };
+
+ // Now create an array adapter and set it to display using our row
+ SimpleCursorAdapter notes =
+ new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
+ setListAdapter(notes);
+ }</pre>
+
+ <p>Here's what we've done:</p>
+ <ol>
+ <li>
+ After obtaining the Cursor from <code>mDbHelper.fetchAllNotes()</code>, we
+ use an Activity method called
+ <code>startManagingCursor()</code> that allows Android to take care of the
+ Cursor lifecycle instead of us needing to worry about it. (We will cover the implications
+ of the lifecycle in exercise 3, but for now just know that this allows Android to do some
+ of our resource management work for us.)</li>
+ <li>
+ Then we create a string array in which we declare the column(s) we want
+ (just the title, in this case), and an int array that defines the View(s)
+ to which we'd like to bind the columns (these should be in order, respective to
+ the string array, but here we only have one for each).</li>
+ <li>
+ Next is the SimpleCursorAdapter instantiation.
+ Like many classes in Android, the SimpleCursorAdapter needs a Context in order to do its
+ work, so we pass in <code>this</code> for the context (since subclasses of Activity
+ implement Context). We pass the <code>notes_row</code> View we created as the receptacle
+ for the data, the Cursor we just created, and then our arrays.</li>
+ </ol>
+ <p>
+ In the future, remember that the mapping between the <strong>from</strong> columns and <strong>to</strong> resources
+ is done using the respective ordering of the two arrays. If we had more columns we wanted
+ to bind, and more Views to bind them in to, we would specify them in order, for example we
+ might use <code>{ NotesDbAdapter.KEY_TITLE, NotesDbAdapter.KEY_BODY }</code> and
+ <code>{ R.id.text1, R.id.text2 }</code> to bind two fields into the row (and we would also need
+ to define text2 in the notes_row.xml, for the body text). This is how you can bind multiple fields
+ into a single row (and get a custom row layout as well).</p>
+ <p>
+ If you get compiler errors about classes not being found, ctrl-shift-O or
+ (cmd-shift-O on the mac) to organize imports.
+ </p>
+
+<h2 style="clear:right;">Step 13</h2>
+ <p>Run it!
+ <ol>
+ <li>
+ Right click on the <code>Notepadv1</code> project.</li>
+ <li>
+ From the popup menu, select <strong>Run As</strong> >
+ <strong>Android Application</strong>.</li>
+ <li>
+ If you see a dialog come up, select Android Launcher as the way of running
+ the application (you can also use the link near the top of the dialog to
+ set this as your default for the workspace; this is recommended as it will
+ stop the plugin from asking you this every time).</li>
+ <li>Add new notes by hitting the menu button and selecting <em>Add
+ Item</em> from the menu.</li>
+ </ol>
+
+<h2 style="clear:right;">Solution and Next Steps</h2>
+ <p>You can see the solution to this class in <code>Notepadv1Solution</code>
+from
+the zip file to compare with your own.</p>
+
+<p>Once you are ready, move on to <a href="notepad-ex2.html">Tutorial
+Exercise 2</a> to add the ability to create, edit and delete notes.</p>
+
diff --git a/docs/html/resources/tutorials/notepad/notepad-ex2.jd b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
new file mode 100644
index 0000000..bab9471
--- /dev/null
+++ b/docs/html/resources/tutorials/notepad/notepad-ex2.jd
@@ -0,0 +1,647 @@
+Rpage.title=Notepad Exercise 2
+parent.title=Notepad Tutorial
+parent.link=index.html
+@jd:body
+
+
+<p><em>In this exercise, you will add a second Activity to your notepad application, to let the user
+create and edit notes. You will also allow the user to delete existing notes through a context menu.
+The new Activity assumes responsibility for creating new notes by
+collecting user input and packing it into a return Bundle provided by the intent. This exercise
+demonstrates:</em></p>
+<ul>
+<li><em>Constructing a new Activity and adding it to the Android manifest</em></li>
+<li><em>Invoking another Activity asynchronously using <code>startActivityForResult()</code></em></li>
+<li><em>Passing data between Activity in Bundle objects</em></li>
+<li><em>How to use a more advanced screen layout</em></li>
+<li><em>How to create a context menu</em></li>
+</ul>
+
+<div style="float:right;white-space:nowrap">
+ [<a href="notepad-ex1.html">Exercise 1</a>]
+ <span style="color:#BBB;">
+ [<a href="notepad-ex2.html" style="color:#DDD;">Exercise 2</a>]
+ </span>
+ [<a href="notepad-ex3.html">Exercise 3</a>]
+ [<a href="notepad-extra-credit.html">Extra Credit</a>]
+</div>
+
+<h2>Step 1</h2>
+
+<p>Create a new Android project using the sources from <code>Notepadv2</code> under the
+<code>NotepadCodeLab</code> folder, just like you did for the first exercise. If you see an error about
+<code>AndroidManifest.xml</code>, or some problems related to an
+<code>android.zip</code> file, right click on the project and select <strong>Android
+Tools</strong> > <strong>Fix Project Properties</strong>.</p>
+
+<p>Open the <code>Notepadv2</code> project and take a look around:</p>
+<ul>
+ <li>
+ Open and look at the <code>strings.xml</code> file under
+ <code>res/values</code> — there are several new strings which we will use
+ for our new functionality
+ </li>
+ <li>
+ Also, open and take a look at the top of the <code>Notepadv2</code> class,
+ you will notice several new constants have been defined along with a new <code>mNotesCursor</code>
+ field used to hold the cursor we are using.
+ </li>
+ <li>
+ Note also that the <code>fillData()</code> method has a few more comments and now uses
+ the new field to store the notes Cursor. The <code>onCreate()</code> method is
+ unchanged from the first exercise. Also notice that the member field used to store the
+ notes Cursor is now called <code>mNotesCursor</code>. The <code>m</code> denotes a member
+ field and is part of the Android coding style standards.
+ </li>
+ <li>
+ There are also a couple of new overridden methods
+ (<code>onCreateContextMenu()</code>, <code>onContextItemSelected()</code>,
+ <code>onListItemClick()</code> and <code>onActivityResult()</code>)
+ which we will be filling in below.
+ </li>
+</ul>
+
+
+<h2>Step 2</h2>
+
+<div class="sidebox">
+<p>Context menus should always be used when performing actions upon specific elements in the UI.
+When you register a View to a context menu, the context menu is revealed by performing a "long-click"
+on the UI component (press and hold the touchscreen or highlight and hold down the selection key for about two seconds).</p>
+</div>
+
+<p>First, let's create the context menu that will allow users to delete individual notes.
+Open the Notepadv2 class.</p>
+
+<ol>
+ <li>In order for each list item in the ListView to register for the context menu, we call
+ <code>registerForContextMenu()</code> and pass it our ListView. So, at the very end of
+ the <code>onCreate()</code> method add this line:
+ <pre>registerForContextMenu(getListView());</pre>
+ <p>Because our Activity extends the ListActivity class, <code>getListView()</code> will return us
+ the local ListView object for the Activity. Now, each list item in this ListView will activate the
+ context menu.
+ <li>
+ Now fill in the <code>onCreateContextMenu()</code> method. This callback is similar to the other
+ menu callback used for the options menu. Here, we add just one line, which will add a menu item
+ to delete a note. Call <code>menu.add()</code> like so:
+ <pre>
+public boolean onCreateContextMenu(Menu menu, View v
+ ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ menu.add(0, DELETE_ID, 0, R.string.menu_delete);
+}</pre>
+ <p>The <code>onCreateContextMenu()</code> callback some passes other information in addition to the Menu object,
+ such as the View that has been triggered for the menu and
+ an extra object that may contain additional information about the object selected. However, we don't care about
+ these here, because we only have one kind of object in the Activity that uses context menus. In the next
+ step, we'll handle the menu item selection.</p>
+ </li>
+</ol>
+
+<h2>Step 3</h2>
+ <p>Now that the we've registered our ListView for a context menu and defined our context menu item, we need
+ to handle the callback when it is selected. For this, we need to identify the list ID of the
+ selected item, then delete it. So fill in the
+ <code>onContextItemSelected()</code> method like this:</p>
+<pre>
+public boolean onContextItemSelected(MenuItem item) {
+ switch(item.getItemId()) {
+ case DELETE_ID:
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
+ mDbHelper.deleteNote(info.id);
+ fillData();
+ return true;
+ }
+ return super.onContextItemSelected(item);
+}</pre>
+<p>Here, we retrieve the {@link android.widget.AdapterView.AdapterContextMenuInfo AdapterContextMenuInfo}
+with {@link android.view.MenuItem#getMenuInfo()}. The <var>id</var> field of this object tells us
+the position of the item in the ListView. We then pass this to the <code>deleteNote()</code>
+method of our NotesDbAdapter and the note is deleted. That's it for the context menu — notes
+can now be deleted.</p>
+
+<h2 style="clear:right;">Step 4</h2>
+<div class="sidebox" style="border:2px solid #FFFFDD;float:right;
+ background-color:#FFFFEE;margin-right:0px;
+ margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
+ <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
+ background-color:#FFFFDD;">Starting Other Activities</h2>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">In this example our Intent uses a class name specifically.
+ As well as
+ <a href="{@docRoot}resources/faq/commontasks.html#intentexamples">starting intents</a> in
+ classes we already know about, be they in our own application or another
+ application, we can also create Intents without knowing exactly which
+ application will handle it.</p>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">For example, we might want to open a page in a
+ browser, and for this we still use
+ an Intent. But instead of specifying a class to handle it, we use
+ a predefined Intent constant, and a content URI that describes what we
+ want to do. See {@link android.content.Intent
+ android.content.Intent} for more information.</p>
+</div>
+
+ <p>Fill in the body of the <code>createNote()</code> method:
+ <p>Create a new <code>Intent</code> to create a note
+ (<code>ACTIVITY_CREATE</code>) using the <code>NoteEdit</code> class.
+ Then fire the Intent using the <code>startActivityForResult()</code> method
+ call:</p>
+ <pre style="overflow:auto">
+Intent i = new Intent(this, NoteEdit.class);
+startActivityForResult(i, ACTIVITY_CREATE);</pre>
+ <p>This form of the Intent call targets a specific class in our Activity, in this case
+ <code>NoteEdit</code>. Since the Intent class will need to communicate with the Android
+ operating system to route requests, we also have to provide a Context (<code>this</code>).</p>
+ <p>The <code>startActivityForResult()</code> method fires the Intent in a way that causes a method
+ in our Activity to be called when the new Activity is completed. The method in our Activity
+ that receives the callback is called
+ <code>onActivityResult()</code> and we will implement it in a later step. The other way
+ to call an Activity is using <code>startActivity()</code> but this is a "fire-and-forget" way
+ of calling it — in this manner, our Activity is not informed when the Activity is completed, and there is
+ no way to return result information from the called Activity with <code>startActivity()</code>.
+ <p>Don't worry about the fact that <code>NoteEdit</code> doesn't exist yet,
+ we will fix that soon. </p>
+ </li>
+
+
+<h2>Step 5</h2>
+
+ <p>Fill in the body of the <code>onListItemClick()</code> override.</p>
+ <p><code>onListItemClick()</code> is a callback method that we'll override. It is called when
+ the user selects an item from the list. It is passed four parameters: the
+ <code>ListView</code> object it was invoked from, the <code>View</code>
+ inside the <code>ListView</code> that was clicked on, the
+ <code>position</code> in the list that was clicked, and the
+ <code>mRowId</code> of the item that was clicked. In this instance we can
+ ignore the first two parameters (we only have one <code>ListView</code> it
+ could be), and we ignore the <code>mRowId</code> as well. All we are
+ interested in is the <code>position</code> that the user selected. We use
+ this to get the data from the correct row, and bundle it up to send to
+ the <code>NoteEdit</code> Activity.</p>
+ <p>In our implementation of the callback, the method creates an
+ <code>Intent</code> to edit the note using
+ the <code>NoteEdit</code> class. It then adds data into the extras Bundle of
+ the Intent, which will be passed to the called Activity. We use it
+ to pass in the title and body text, and the <code>mRowId</code> for the note we are
+ editing. Finally, it will fire the Intent using the
+ <code>startActivityForResult()</code> method call. Here's the code that
+ belongs in <code>onListItemClick()</code>:</p>
+ <pre>
+super.onListItemClick(l, v, position, id);
+Cursor c = mNotesCursor;
+c.moveToPosition(position);
+Intent i = new Intent(this, NoteEdit.class);
+i.putExtra(NotesDbAdapter.KEY_ROWID, id);
+i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
+ c.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
+i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
+ c.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
+startActivityForResult(i, ACTIVITY_EDIT);</pre>
+ <ul>
+ <li>
+ <code>putExtra()</code> is the method to add items into the extras Bundle
+ to pass in to intent invocations. Here, we are
+ using the Bundle to pass in the title, body and mRowId of the note we want to edit.
+ </li>
+ <li>
+ The details of the note are pulled out from our query Cursor, which we move to the
+ proper position for the element that was selected in the list, with
+ the <code>moveToPosition()</code> method.</li>
+ <li>With the extras added to the Intent, we invoke the Intent on the
+ <code>NoteEdit</code> class by passing <code>startActivityForResult()</code>
+ the Intent and the request code. (The request code will be
+ returned to <code>onActivityResult</code> as the <code>requestCode</code> parameter.)</li>
+ </ul>
+ <p class="note"><b>Note:</b> We assign the mNotesCursor field to a local variable at the
+ start of the method. This is done as an optimization of the Android code. Accessing a local
+ variable is much more efficient than accessing a field in the Dalvik VM, so by doing this
+ we make only one access to the field, and five accesses to the local variable, making the
+ routine much more efficient. It is recommended that you use this optimization when possible.</p>
+
+
+<h2>Step 6</h2>
+
+<p>The above <code>createNote()</code> and <code>onListItemClick()</code>
+ methods use an asynchronous Intent invocation. We need a handler for the callback, so here we fill
+ in the body of the <code>onActivityResult()</code>. </p>
+<p><code>onActivityResult()</code> is the overridden method
+ which will be called when an Activity returns with a result. (Remember, an Activity
+ will only return a result if launched with <code>startActivityForResult</code>.) The parameters provided
+ to the callback are: </p>
+ <ul>
+ <li><code>requestCode</code> — the original request code
+ specified in the Intent invocation (either <code>ACTIVITY_CREATE</code> or
+ <code>ACTIVITY_EDIT</code> for us).
+ </li>
+ <li><code>resultCode</code> — the result (or error code) of the call, this
+ should be zero if everything was OK, but may have a non-zero code indicating
+ that something failed. There are standard result codes available, and you
+ can also create your own constants to indicate specific problems.
+ </li>
+ <li><code>intent</code> — this is an Intent created by the Activity returning
+ results. It can be used to return data in the Intent "extras."
+ </li>
+ </ul>
+ <p>The combination of <code>startActivityForResult()</code> and
+ <code>onActivityResult()</code> can be thought of as an asynchronous RPC
+ (remote procedure call) and forms the recommended way for an Activity to invoke
+ another and share services.</p>
+ <p>Here's the code that belongs in your <code>onActivityResult()</code>:</p>
+ <pre>
+super.onActivityResult(requestCode, resultCode, intent);
+Bundle extras = intent.getExtras();
+
+switch(requestCode) {
+case ACTIVITY_CREATE:
+ String title = extras.getString(NotesDbAdapter.KEY_TITLE);
+ String body = extras.getString(NotesDbAdapter.KEY_BODY);
+ mDbHelper.createNote(title, body);
+ fillData();
+ break;
+case ACTIVITY_EDIT:
+ Long mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
+ if (mRowId != null) {
+ String editTitle = extras.getString(NotesDbAdapter.KEY_TITLE);
+ String editBody = extras.getString(NotesDbAdapter.KEY_BODY);
+ mDbHelper.updateNote(mRowId, editTitle, editBody);
+ }
+ fillData();
+ break;
+}</pre>
+
+ <ul>
+ <li>
+ We are handling both the <code>ACTIVITY_CREATE</code> and
+ <code>ACTIVITY_EDIT</code> activity results in this method.
+ </li>
+ <li>
+ In the case of a create, we pull the title and body from the extras (retrieved from the
+ returned Intent) and use them to create a new note.
+ </li>
+ <li>
+ In the case of an edit, we pull the mRowId as well, and use that to update
+ the note in the database.
+ </li>
+ <li>
+ <code>fillData()</code> at the end ensures everything is up to date .
+ </li>
+ </ul>
+
+
+<h2>Step 7</h2>
+
+ <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
+ background-color:#FFFFEE;margin-right:0px;
+ margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
+ <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
+ background-color:#FFFFDD;">The Art of Layout</h2>
+ <p style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">The provided
+ note_edit.xml layout file is the most sophisticated one in the application we will be building,
+ but that doesn't mean it is even close to the kind of sophistication you will be likely to want
+ in real Android applications.</p>
+ <p style="padding-left:.5em;font-size:12px;margin:0; padding:.0em .5em .5em 1em;">Creating a
+ good UI is part art and part science, and the rest is work. Mastery of <a
+ href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a> is an essential part of creating
+ a good looking Android application.</p>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">Take a look at the
+ <a href="{@docRoot}resources/tutorials/views/index.html">Hello Views</a>
+ for some example layouts and how to use them. The ApiDemos sample project is also a
+ great resource from which to learn how to create different layouts.</p>
+ </div>
+
+<p>Open the file <code>note_edit.xml</code> that has been provided and take a
+ look at it. This is the UI code for the Note Editor.</p>
+ <p>This is the most
+ sophisticated UI we have dealt with yet. The file is given to you to avoid
+ problems that may sneak in when typing the code. (The XML is very strict
+ about case sensitivity and structure, mistakes in these are the usual cause
+ of problems with layout.)</p>
+ <p>There is a new parameter used
+ here that we haven't seen before: <code>android:layout_weight</code> (in
+ this case set to use the value 1 in each case).</p>
+ <p><code>layout_weight</code> is used in LinearLayouts
+ to assign "importance" to Views within the layout. All Views have a default
+ <code>layout_weight</code> of zero, meaning they take up only as much room
+ on the screen as they need to be displayed. Assigning a value higher than
+ zero will split up the rest of the available space in the parent View, according
+ to the value of each View's <code>layout_weight</code> and its ratio to the
+ overall <code>layout_weight</code> specified in the current layout for this
+ and other View elements.</p>
+ <p>To give an example: let's say we have a text label
+ and two text edit elements in a horizontal row. The label has no
+ <code>layout_weight</code> specified, so it takes up the minimum space
+ required to render. If the <code>layout_weight</code> of each of the two
+ text edit elements is set to 1, the remaining width in the parent layout will
+ be split equally between them (because we claim they are equally important).
+ If the first one has a <code>layout_weight</code> of 1
+ and the second has a <code>layout_weight</code> of 2, then one third of the
+ remaining space will be given to the first, and two thirds to the
+ second (because we claim the second one is more important).</p>
+ <p>This layout also demonstrates how to nest multiple layouts
+ inside each other to achieve a more complex and pleasant layout. In this
+ example, a horizontal linear layout is nested inside the vertical one to
+ allow the title label and text field to be alongside each other,
+ horizontally.</p>
+
+
+<h2 style="clear:right;">Step 8</h2>
+
+ <p>Create a <code>NoteEdit</code> class that extends
+ <code>android.app.Activity</code>.</p>
+ <p>This is the first time we will have
+ created an Activity without the Android Eclipse plugin doing it for us. When
+ you do so, the <code>onCreate()</code> method is not automatically
+ overridden for you. It is hard to imagine an Activity that doesn't override
+ the <code>onCreate()</code> method, so this should be the first thing you do.</p>
+ <ol>
+ <li>Right click on the <code>com.android.demo.notepad2</code> package
+ in the Package Explorer, and select <strong>New</strong> > <strong>Class</strong> from the popup
+ menu.</li>
+ <li>Fill in <code>NoteEdit</code> for the <code>Name:</code> field in the
+ dialog.</li>
+ <li>In the <code>Superclass:</code> field, enter
+ <code>android.app.Activity</code> (you can also just type Activity and hit
+ Ctrl-Space on Windows and Linux or Cmd-Space on the Mac, to invoke code
+ assist and find the right package and class).</li>
+ <li>Click <strong>Finish</strong>.</li>
+ <li>In the resulting <code>NoteEdit</code> class, right click in the editor
+ window and select <strong>Source</strong> > <strong>Override/Implement Methods...</strong></li>
+ <li>Scroll down through the checklist in the dialog until you see
+ <code>onCreate(Bundle)</code> — and check the box next to it.</li>
+ <li>Click <strong>OK</strong>.<p>The method should now appear in your class.</p></li>
+ </ol>
+
+<h2>Step 9</h2>
+
+<p>Fill in the body of the <code>onCreate()</code> method for <code>NoteEdit</code>.</p>
+
+<p>This will set the title of our new Activity to say "Edit Note" (one
+ of the strings defined in <code>strings.xml</code>). It will also set the
+ content view to use our <code>note_edit.xml</code> layout file. We can then
+ grab handles to the title and body text edit views, and the confirm button,
+ so that our class can use them to set and get the note title and body,
+ and attach an event to the confirm button for when it is pressed by the
+ user.</p>
+ <p>We can then unbundle the values that were passed in to the Activity
+ with the extras Bundle attached to the calling Intent. We'll use them to pre-populate
+ the title and body text edit views so that the user can edit them.
+ Then we will grab and store the <code>mRowId</code> so we can keep
+ track of what note the user is editing.</p>
+
+ <ol>
+ <li>
+ Inside <code>onCreate()</code>, set up the layout:<br>
+ <pre>setContentView(R.layout.note_edit);</pre>
+ </li>
+ <li>
+ Find the edit and 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,
+ and <code>Button</code> for the confirm button):</p>
+ <pre>
+mTitleText = (EditText) findViewById(R.id.title);
+mBodyText = (EditText) findViewById(R.id.body);
+Button confirmButton = (Button) findViewById(R.id.confirm);</pre>
+ <p>Note that <code>mTitleText</code> and <code>mBodyText</code> are member
+ fields (you need to declare them at the top of the class definition).</p>
+ </li>
+ <li>At the top of the class, declare a <code>Long mRowId</code> private field to store
+ the current <code>mRowId</code> being edited (if any).
+ </li>
+ <li>Continuing inside <code>onCreate()</code>,
+ add code to initialize the <code>title</code>, <code>body</code> and
+ <code>mRowId</code> from the extras Bundle in
+ the Intent (if it is present):<br>
+ <pre>
+mRowId = null;
+Bundle extras = getIntent().getExtras();
+if (extras != null) {
+ String title = extras.getString(NotesDbAdapter.KEY_TITLE);
+ String body = extras.getString(NotesDbAdapter.KEY_BODY);
+ mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
+
+ if (title != null) {
+ mTitleText.setText(title);
+ }
+ if (body != null) {
+ mBodyText.setText(body);
+ }
+}</pre>
+ <ul>
+ <li>
+ We are pulling the <code>title</code> and
+ <code>body</code> out of the
+ <code>extras</code> Bundle that was set from the
+ Intent invocation.
+ </li><li>
+ We also null-protect the text field setting (i.e., we don't want to set
+ the text fields to null accidentally).</li>
+ </ul>
+ </li>
+ <li>
+ Create an <code>onClickListener()</code> for the button:
+ <p>Listeners can be one of the more confusing aspects of UI
+ implementation, but
+ what we are trying to achieve in this case is simple. We want an
+ <code>onClick()</code> method to be called when the user presses the
+ confirm button, and use that to do some work and return the values
+ of the edited note to the Intent caller. We do this using something called
+ an anonymous inner class. This is a bit confusing to look at unless you
+ have seen them before, but all you really need to take away from this is
+ that you can refer to this code in the future to see how to create a
+ listener and attach it to a button. (Listeners are a common idiom
+ in Java development, particularly for user interfaces.) Here's the empty listener:<br>
+ <pre>
+confirmButton.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View view) {
+
+ }
+
+});</pre>
+ </li>
+ </ol>
+<h2>Step 10</h2>
+
+<p>Fill in the body of the <code>onClick()</code> method of the <code>OnClickListener</code> created in the last step.</p>
+
+ <p>This is the code that will be run when the user clicks on the
+ confirm button. We want this to grab the title and body text from the edit
+ text fields, and put them into the return Bundle so that they can be passed
+ back to the Activity that invoked this <code>NoteEdit</code> Activity. If the
+ operation is an edit rather than a create, we also want to put the
+ <code>mRowId</code> into the Bundle so that the
+ <code>Notepadv2</code> class can save the changes back to the correct
+ note.</p>
+ <ol>
+ <li>
+ Create a <code>Bundle</code> and put the title and body text into it using the
+ constants defined in Notepadv2 as keys:<br>
+ <pre>
+Bundle bundle = new Bundle();
+
+bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
+bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
+if (mRowId != null) {
+ bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
+}</pre>
+ </li>
+ <li>
+ Set the result information (the Bundle) in a new Intent and finish the Activity:
+ <pre>
+Intent mIntent = new Intent();
+mIntent.putExtras(bundle);
+setResult(RESULT_OK, mIntent);
+finish();</pre>
+ <ul>
+ <li>The Intent is simply our data carrier that carries our Bundle
+ (with the title, body and mRowId).</li>
+ <li>The <code>setResult()</code> method is used to set the result
+ code and return Intent to be passed back to the
+ Intent caller. In this case everything worked, so we return RESULT_OK for the
+ result code.</li>
+ <li>The <code>finish()</code> call is used to signal that the Activity
+ is done (like a return call). Anything set in the Result will then be
+ returned to the caller, along with execution control.</li>
+ </ul>
+ </li>
+ </ol>
+ <p>The full <code>onCreate()</code> method (plus supporting class fields) should
+ now look like this:</p>
+ <pre>
+private EditText mTitleText;
+private EditText mBodyText;
+private Long mRowId;
+
+@Override
+protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.note_edit);
+
+ mTitleText = (EditText) findViewById(R.id.title);
+ mBodyText = (EditText) findViewById(R.id.body);
+
+ Button confirmButton = (Button) findViewById(R.id.confirm);
+
+ mRowId = null;
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ String title = extras.getString(NotesDbAdapter.KEY_TITLE);
+ String body = extras.getString(NotesDbAdapter.KEY_BODY);
+ mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
+
+ if (title != null) {
+ mTitleText.setText(title);
+ }
+ if (body != null) {
+ mBodyText.setText(body);
+ }
+ }
+
+ confirmButton.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View view) {
+ Bundle bundle = new Bundle();
+
+ bundle.putString(NotesDbAdapter.KEY_TITLE, mTitleText.getText().toString());
+ bundle.putString(NotesDbAdapter.KEY_BODY, mBodyText.getText().toString());
+ if (mRowId != null) {
+ bundle.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
+ }
+
+ Intent mIntent = new Intent();
+ mIntent.putExtras(bundle);
+ setResult(RESULT_OK, mIntent);
+ finish();
+ }
+ });
+}</pre>
+ </li>
+ </ol>
+
+<h2>Step 11</h2>
+
+<div class="sidebox" style="border:2px solid #FFFFDD;float:right;
+ background-color:#FFFFEE;margin-right:0px;
+ margin-bottom:.5em;margin-top:1em;padding:0em;width:240px;">
+ <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
+ background-color:#FFFFDD;">The All-Important Android Manifest File</h2>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">The AndroidManifest.xml file is the way in which Android sees your
+ application. This file defines the category of the application, where
+ it shows up (or even if it shows up) in the launcher or settings, what
+ activities, services, and content providers it defines, what intents it can
+ receive, and more. </p>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">For more information, see the reference document
+ <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">The AndroidManifest.xml File</a></p>
+ </div>
+
+<p>Finally, the new Activity has to be defined in the manifest file:</p>
+ <p>Before the new Activity can be seen by Android, it needs its own
+ Activity entry in the <code>AndroidManifest.xml</code> file. This is to let
+ the system know that it is there and can be called. We could also specify
+ which IntentFilters the activity implements here, but we are going to skip
+ this for now and just let Android know that the Activity is
+ defined.</p>
+ <p>There is a Manifest editor included in the Eclipse plugin that makes it much easier
+ to edit the AndroidManifest file, and we will use this. If you prefer to edit the file directly
+ or are not using the Eclipse plugin, see the box at the end for information on how to do this
+ without using the new Manifest editor.<p>
+ <ol>
+ <li>Double click on the <code>AndroidManifest.xml</code> file in the package explorer to open it.
+ </li>
+ <li>Click the <strong>Application</strong> tab at the bottom of the Manifest editor.</li>
+ <li>Click <strong>Add...</strong> in the Application Nodes section.
+ <p>If you see a dialog with radiobuttons at the top, select the top radio button:
+ "Create a new element at the top level, in Application".</p></li>
+ <li>Make sure "(A) Activity" is selected in the selection pane of the dialog, and click <strong>OK</strong>.</li>
+ <li>Click on the new "Activity" node, in the Application Nodes section, then
+ type <code>.NoteEdit</code> into the <em>Name*</em>
+ field to the right. Press Return/Enter.</li>
+ </ol>
+ <p>The Android Manifest editor helps you add more complex entries into the AndroidManifest.xml
+ file, have a look around at some of the other options available (but be careful not to select
+ them otherwise they will be added to your Manifest). This editor should help you understand
+ and alter the AndroidManifest.xml file as you move on to more advanced Android applications.</p>
+
+ <p class="note">If you prefer to edit this file directly, simply open the
+ <code>AndroidManifest.xml</code> file and look at the source (use the
+ <code>AndroidManifest.xml</code> tab in the eclipse editor to see the source code directly).
+ Then edit the file as follows:<br>
+ <code><activity android:name=".NoteEdit"></activity></code><br><br>
+ This should be placed just below the line that reads:<br>
+ <code></activity></code> for the <code>.Notepadv2</code> activity.</p>
+
+<h2 style="clear:right;">Step 12</h2>
+
+<p>Now Run it!</p>
+<p>You should now be able to add real notes from
+the menu, as well as delete an existing one. Notice that in order to delete, you must
+first use the directional controls on the device to highlight the note.
+Furthermore, selecting a note title from the list should bring up the note
+editor to let you edit it. Press confirm when finished to save the changes
+back to the database.
+
+<h2>Solution and Next Steps</h2>
+
+<p>You can see the solution to this exercise in <code>Notepadv2Solution</code>
+from the zip file to compare with your own.</p>
+<p>Now try editing a note, and then hitting the back button on the emulator
+instead of the confirm button (the back button is below the menu button). You
+will see an error come up. Clearly our application still has some problems.
+Worse still, if you did make some changes and hit the back button, when you go
+back into the notepad to look at the note you changed, you will find that all
+your changes have been lost. In the next exercise we will fix these
+problems.</p>
+
+<p>
+Once you are ready, move on to <a href="notepad-ex3.html">Tutorial
+Exercise 3</a> where you will fix the problems with the back button and lost
+edits by introducing a proper life cycle into the NoteEdit Activity.</p>
+
+
diff --git a/docs/html/resources/tutorials/notepad/notepad-ex3.jd b/docs/html/resources/tutorials/notepad/notepad-ex3.jd
new file mode 100644
index 0000000..8737280
--- /dev/null
+++ b/docs/html/resources/tutorials/notepad/notepad-ex3.jd
@@ -0,0 +1,358 @@
+page.title=Notepad Exercise 3
+parent.title=Notepad Tutorial
+parent.link=index.html
+@jd:body
+
+
+<p><em>In this exercise, you will use life-cycle event callbacks to store and
+retrieve application state data. This exercise demonstrates:</em></p>
+<ul>
+<li><em>Life-cycle events and how your application can use them</em></li>
+<li><em>Techniques for maintaining application state</em></li>
+</ul>
+
+<div style="float:right;white-space:nowrap">
+ [<a href="notepad-ex1.html">Exercise 1</a>]
+ [<a href="notepad-ex2.html">Exercise 2</a>]
+ <span style="color:#BBB;">
+ [<a href="notepad-ex3.html" style="color:#BBB;">Exercise 3</a>]
+ </span>
+ [<a href="notepad-extra-credit.html">Extra Credit</a>]
+</div>
+
+<h2>Step 1</h2>
+
+<p>Import <code>Notepadv3</code> into Eclipse. If you see an error about
+<code>AndroidManifest.xml,</code> or some problems related to an Android zip
+file, right click on the project and select <strong>Android Tools</strong> >
+<strong>Fix Project Properties</strong> from the popup menu. The starting point for this exercise is
+exactly where we left off at the end of the Notepadv2. </p>
+<p>The current application has some problems — hitting the back button when editing
+causes a crash, and anything else that happens during editing will cause the
+edits to be lost.</p>
+<p>To fix this, we will move most of the functionality for creating and editing
+the note into the NoteEdit class, and introduce a full life cycle for editing
+notes.</p>
+
+ <ol>
+ <li>Remove the code in <code>NoteEdit</code> that parses the title and body
+ from the extras Bundle.
+ <p>Instead, we are going to use the <code>DBHelper</code> class
+ to access the notes from the database directly. All we need passed into the
+ NoteEdit Activity is a <code>mRowId</code> (but only if we are editing, if creating we pass
+ nothing). Remove these lines:</p>
+ <pre>
+String title = extras.getString(NotesDbAdapter.KEY_TITLE);
+String body = extras.getString(NotesDbAdapter.KEY_BODY);</pre>
+ </li>
+ <li>We will also get rid of the properties that were being passed in
+ the <code>extras</code> Bundle, which we were using to set the title
+ and body text edit values in the UI. So delete:
+ <pre>
+if (title != null) {
+ mTitleText.setText(title);
+}
+if (body != null) {
+ mBodyText.setText(body);
+}</pre>
+ </li>
+ </ol>
+
+<h2>Step 2</h2>
+
+<p>Create a class field for a <code>NotesDbAdapter</code> at the top of the NoteEdit class:</p>
+ <pre> private NotesDbAdapter mDbHelper;</pre>
+<p>Also add an instance of <code>NotesDbAdapter</code> in the
+ <code>onCreate()</code> method (right below the <code>super.onCreate()</code> call):</p>
+ <pre>
+ mDbHelper = new NotesDbAdapter(this);<br>
+ mDbHelper.open();</pre>
+
+<h2>Step 3</h2>
+
+<p>In <code>NoteEdit</code>, we need to check the <var>savedInstanceState</var> for the
+<code>mRowId</code>, in case the note
+ editing contains a saved state in the Bundle, which we should recover (this would happen
+ if our Activity lost focus and then restarted).</p>
+ <ol>
+ <li>
+ Replace the code that currently initializes the <code>mRowId</code>:<br>
+ <pre>
+ mRowId = null;
+
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ mRowId = extras.getLong(NotesDbAdapter.KEY_ROWID);
+ }
+ </pre>
+ with this:
+ <pre>
+ mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID)
+ : null;
+ if (mRowId == null) {
+ Bundle extras = getIntent().getExtras();
+ mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
+ : null;
+ }
+ </pre>
+ </li>
+ <li>
+ Note the null check for <code>savedInstanceState</code>, and we still need to load up
+ <code>mRowId</code> from the <code>extras</code> Bundle if it is not
+ provided by the <code>savedInstanceState</code>. This is a ternary operator shorthand
+ to safely either use the value or null if it is not present.
+ </li>
+ </ol>
+
+<h2>Step 4</h2>
+
+<p>Next, we need to populate the fields based on the <code>mRowId</code> if we
+ have it:</p>
+ <pre>populateFields();</pre>
+ <p>This goes before the <code>confirmButton.setOnClickListener()</code> line.
+ We'll define this method in a moment.</p>
+
+<h2>Step 5</h2>
+
+<p>Get rid of the Bundle creation and Bundle value settings from the
+ <code>onClick()</code> handler method. The Activity no longer needs to
+ return any extra information to the caller. And because we no longer have
+ an Intent to return, we'll use the shorter version
+ of <code>setResult()</code>:</p>
+ <pre>
+public void onClick(View view) {
+ setResult(RESULT_OK);
+ finish();
+}</pre>
+ <p>We will take care of storing the updates or new notes in the database
+ ourselves, using the life-cycle methods.</p>
+
+ <p>The whole <code>onCreate()</code> method should now look like this:</p>
+ <pre>
+super.onCreate(savedInstanceState);
+
+mDbHelper = new NotesDbAdapter(this);
+mDbHelper.open();
+
+setContentView(R.layout.note_edit);
+
+mTitleText = (EditText) findViewById(R.id.title);
+mBodyText = (EditText) findViewById(R.id.body);
+
+Button confirmButton = (Button) findViewById(R.id.confirm);
+
+mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotesDbAdapter.KEY_ROWID)
+ : null;
+if (mRowId == null) {
+ Bundle extras = getIntent().getExtras();
+ mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
+ : null;
+}
+
+populateFields();
+
+confirmButton.setOnClickListener(new View.OnClickListener() {
+
+ public void onClick(View view) {
+ setResult(RESULT_OK);
+ finish();
+ }
+
+});</pre>
+
+<h2>Step 6</h2>
+
+<p>Define the <code>populateFields()</code> method.</p>
+ <pre>
+private void populateFields() {
+ if (mRowId != null) {
+ Cursor note = mDbHelper.fetchNote(mRowId);
+ startManagingCursor(note);
+ mTitleText.setText(note.getString(
+ note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
+ mBodyText.setText(note.getString(
+ note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
+ }
+}</pre>
+<p>This method uses the <code>NotesDbAdapter.fetchNote()</code> method to find the right note to
+edit, then it calls <code>startManagingCursor()</code> from the <code>Activity</code> class, which
+is an Android convenience method provided to take care of the Cursor life-cycle. This will release
+and re-create resources as dictated by the Activity life-cycle, so we don't need to worry about
+doing that ourselves. After that, we just look up the title and body values from the Cursor
+and populate the View elements with them.</p>
+
+
+<h2>Step 7</h2>
+
+ <div class="sidebox" style="border:2px solid #FFFFDD;float:right;
+ background-color:#FFFFEE;margin-right:0px;margin-bottom:.5em;
+ margin-top:1em;padding:0em;width:240px;">
+ <h2 style="border:0;font-size:12px;padding:.5em .5em .5em 1em;margin:0;
+ background-color:#FFFFDD;">Why handling life-cycle events is important</h2>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">If you are used to always having control in your applications, you
+ might not understand why all this life-cycle work is necessary. The reason
+ is that in Android, you are not in control of your Activity, the
+ operating system is!</p>
+ <p style="padding-left:.5em;font-size:12px;margin:0;
+ padding:.0em .5em .5em 1em;">As we have already seen, the Android model is based around activities
+ calling each other. When one Activity calls another, the current Activity
+ is paused at the very least, and may be killed altogether if the
+ system starts to run low on resources. If this happens, your Activity will
+ have to store enough state to come back up later, preferably in the same
+ state it was in when it was killed.</p>
+ <p style="padding-left:.5em;font-size:12px;margin:0;padding:.0em .5em .5em 1em;">
+ Android has a <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">well-defined life cycle</a>.
+ Lifecycle events can happen even if you are not handing off control to
+ another Activity explicitly. For example, perhaps a call comes in to the
+ handset. If this happens, and your Activity is running, it will be swapped
+ out while the call Activity takes over.</p>
+ </div>
+
+<p>Still in the <code>NoteEdit</code> class, we now override the methods
+ <code>onSaveInstanceState()</code>, <code>onPause()</code> and
+ <code>onResume()</code>. These are our life-cycle methods
+ (along with <code>onCreate()</code> which we already have).</p>
+
+<p><code>onSaveInstanceState()</code> is called by Android if the
+ Activity is being stopped and <strong>may be killed before it is
+ resumed!</strong> This means it should store any state necessary to
+ re-initialize to the same condition when the Activity is restarted. It is
+ the counterpart to the <code>onCreate()</code> method, and in fact the
+ <code>savedInstanceState</code> Bundle passed in to <code>onCreate()</code> is the same
+ Bundle that you construct as <code>outState</code> in the
+ <code>onSaveInstanceState()</code> method.</p>
+
+<p><code>onPause()</code> and <code>onResume()</code> are also
+ complimentary methods. <code>onPause()</code> is always called when the
+ Activity ends, even if we instigated that (with a <code>finish()</code> call for example).
+ We will use this to save the current note back to the database. Good
+ practice is to release any resources that can be released during an
+ <code>onPause()</code> as well, to take up less resources when in the
+ passive state. <code>onResume()</code> will call our <code>populateFields()</code> method
+ to read the note out of the database again and populate the fields.</p>
+
+<p>So, add some space after the <code>populateFields()</code> method
+ and add the following life-cycle methods:</p>
+ <ol type="a">
+ <li><code>
+ onSaveInstanceState()</code>:
+ <pre>
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putLong(NotesDbAdapter.KEY_ROWID, mRowId);
+ }</pre>
+ </li>
+ <li><code>
+ onPause()</code>:
+ <pre>
+ @Override
+ protected void onPause() {
+ super.onPause();
+ saveState();
+ }</pre>
+ <p>We'll define <code>saveState()</code> next.</p>
+ </li>
+ <li><code>
+ onResume()</code>:
+ <pre>
+ @Override
+ protected void onResume() {
+ super.onResume();
+ populateFields();
+ }</pre>
+ </li>
+ </ol>
+
+
+<h2 style="clear:right;">Step 8</h2>
+
+<p>Define the <code>saveState()</code> method to put the data out to the
+database.</p>
+ <pre>
+ private void saveState() {
+ String title = mTitleText.getText().toString();
+ String body = mBodyText.getText().toString();
+
+ if (mRowId == null) {
+ long id = mDbHelper.createNote(title, body);
+ if (id > 0) {
+ mRowId = id;
+ }
+ } else {
+ mDbHelper.updateNote(mRowId, title, body);
+ }
+ }</pre>
+ <p>Note that we capture the return value from <code>createNote()</code> and if a valid row ID is
+ returned, we store it in the <code>mRowId</code> field so that we can update the note in future
+ rather than create a new one (which otherwise might happen if the life-cycle events are
+ triggered).</p>
+
+
+<h2 style="clear:right;">Step 9</h2>
+
+<p>Now pull out the previous handling code from the
+ <code>onActivityResult()</code> method in the <code>Notepadv3</code>
+ class.</p>
+<p>All of the note retrieval and updating now happens within the
+ <code>NoteEdit</code> life cycle, so all the <code>onActivityResult()</code>
+ method needs to do is update its view of the data, no other work is
+ necessary. The resulting method should look like this:</p>
+<pre>
+@Override
+protected void onActivityResult(int requestCode, int resultCode,
+ Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
+ fillData();
+}</pre>
+
+<p>Because the other class now does the work, all this has to do is refresh
+ the data.</p>
+
+<h2>Step 10</h2>
+
+<p>Also remove the lines which set the title and body from the
+ <code>onListItemClick()</code> method (again they are no longer needed,
+ only the <code>mRowId</code> is):</p>
+<pre>
+ Cursor c = mNotesCursor;
+ c.moveToPosition(position);</pre>
+<br>
+and also remove:
+<br>
+<pre>
+ i.putExtra(NotesDbAdapter.KEY_TITLE, c.getString(
+ c.getColumnIndex(NotesDbAdapter.KEY_TITLE)));
+ i.putExtra(NotesDbAdapter.KEY_BODY, c.getString(
+ c.getColumnIndex(NotesDbAdapter.KEY_BODY)));</pre>
+<br>
+so that all that should be left in that method is:
+<br>
+<pre>
+ super.onListItemClick(l, v, position, id);
+ Intent i = new Intent(this, NoteEdit.class);
+ i.putExtra(NotesDbAdapter.KEY_ROWID, id);
+ startActivityForResult(i, ACTIVITY_EDIT);</pre>
+
+ <p>You can also now remove the mNotesCursor field from the class, and set it back to using
+ a local variable in the <code>fillData()</code> method:
+<br><pre>
+ Cursor notesCursor = mDbHelper.fetchAllNotes();</pre></p>
+ <p>Note that the <code>m</code> in <code>mNotesCursor</code> denotes a member field, so when we
+ make <code>notesCursor</code> a local variable, we drop the <code>m</code>. Remember to rename the
+ other occurrences of <code>mNotesCursor</code> in your <code>fillData()</code> method.
+</ol>
+<p>
+Run it! (use <em>Run As -> Android Application</em> on the project right
+click menu again)</p>
+
+<h2>Solution and Next Steps</h2>
+
+<p>You can see the solution to this exercise in <code>Notepadv3Solution</code>
+from
+the zip file to compare with your own.</p>
+<p>
+When you are ready, move on to the <a href="notepad-extra-credit.html">Tutorial
+Extra Credit</a> exercise, where you can use the Eclipse debugger to
+examine the life-cycle events as they happen.</p>
diff --git a/docs/html/resources/tutorials/notepad/notepad-extra-credit.jd b/docs/html/resources/tutorials/notepad/notepad-extra-credit.jd
new file mode 100644
index 0000000..0d59b56
--- /dev/null
+++ b/docs/html/resources/tutorials/notepad/notepad-extra-credit.jd
@@ -0,0 +1,70 @@
+page.title=Notepad Extra Credit
+parent.title=Notepad Tutorial
+parent.link=index.html
+@jd:body
+
+
+<p><em>In this exercise, you will use the debugger to look at the work you did
+in Exercise 3. This exercise demonstrates:</em></p>
+<ul>
+<li><em>How to set breakpoints to observe execution</em> </li>
+<li><em>How to run your application in debug mode</code></em></li>
+</ul>
+
+<div style="float:right;white-space:nowrap">
+
+ [<a href="notepad-ex1.html">Exercise 1</a>]
+ [<a href="notepad-ex2.html">Exercise 2</a>]
+ [<a href="notepad-ex3.html">Exercise 3</a>]
+ <span style="color:#BBB;">
+ [<a href="notepad-extra-credit.html" style="color:#BBB;">Extra Credit</a>]
+ </span>
+</div>
+
+<h2>Step 1</h2>
+
+<p>Using the working <code>Notepadv3</code>, put breakpoints in the code at the
+ beginning of the <code>onCreate()</code>, <code>onPause()</code>,
+ <code>onSaveInstanceState()</code> and <code>onResume()</code> methods in the
+ <code>NoteEdit</code> class (if you are not familiar with Eclipse, just
+ right click in the narrow grey border on the left of the edit window at the
+ line you want a breakpoint, and select <em>Toggle Breakpoint</em>, you
+should see a blue dot appear).</p>
+
+<h2>Step 2</h2>
+
+<p>Now start the notepad demo in debug mode:</p>
+
+<ol type="a">
+ <li>
+ Right click on the <code>Notepadv3</code> project and from the Debug menu
+ select <em>Debug As -> Android Application.</em></li>
+ <li>
+ The Android emulator should say <em>"waiting for debugger to connect"</em>
+ briefly and then run the application.</li>
+ <li>
+ If it gets stuck on the waiting... screen, quit the emulator and Eclipse,
+ from the command line do an <code>adb kill-server</code>, and then restart
+Eclipse and try again.</li></ol>
+
+ <h2>Step 3</h2>
+
+<p>When you edit or create a new note you should see the breakpoints getting
+ hit and the execution stopping.</p>
+
+ <h2>Step 4</h2>
+
+<p>Hit the Resume button to let execution continue (yellow rectangle with a
+green triangle to its right in the Eclipse toolbars near the top).</p>
+
+<h2>Step 5</h2>
+
+<p>Experiment a bit with the confirm and back buttons, and try pressing Home and
+ making other mode changes. Watch what life-cycle events are generated and
+when.</p>
+
+<p>The Android Eclipse plugin not only offers excellent debugging support for
+your application development, but also superb profiling support. You can also
+try using <a href="{@docRoot}guide/developing/tools/traceview.html">Traceview</a> to profile your application. If your application is running too slow, this can help you
+find the bottlenecks and fix them.</p>
+
diff --git a/docs/html/resources/tutorials/notepad/notepad-index.jd b/docs/html/resources/tutorials/notepad/notepad-index.jd
new file mode 100644
index 0000000..151c50d
--- /dev/null
+++ b/docs/html/resources/tutorials/notepad/notepad-index.jd
@@ -0,0 +1,143 @@
+page.title=Notepad Tutorial
+@jd:body
+
+
+<p>The tutorial in this section gives you a "hands-on" introduction
+to the Android framework and the tools you use to build applications on it.
+Starting from a preconfigured project file, it guides you through the process of
+developing a simple notepad application and provides concrete examples of how to
+set up the project, develop the application logic and user interface, and then
+compile and run the application. </p>
+
+<p>The tutorial presents the notepad application development as a set of
+exercises (see below), each consisting of several steps. You can follow along
+with the steps in each exercise and gradually build up and refine your
+application. The exercises explain each step in detail and provide all the
+sample code you need to complete the application. </p>
+
+<p>When you are finished with the tutorial, you will have created a functioning
+Android application and learned in depth about many of the most important
+concepts in Android development. If you want to add more complex features to
+your application, you can examine the code in an alternative implementation
+of a notepad application, in the
+<a href="{@docRoot}samples/NotePad/index.html">Sample Code</a> documentation. </p>
+
+
+<a name="who"></a>
+<h2>Who Should Use this Tutorial</h2>
+
+<p>This tutorial is designed for experienced developers, especially those with
+knowledge of the Java programming language. If you haven't written Java
+applications before, you can still use the tutorial, but you might need to work
+at a slower pace. </p>
+
+<p>The tutorial assumes that you have some familiarity with the basic Android
+application concepts and terminology. If you aren't yet familiar with those, you
+should read <a href="{@docRoot}intro/anatomy.html">Overview of an Android
+Application</a> before continuing. </p>
+
+<p>Also note that this tutorial uses
+the Eclipse development environment, with the Android plugin installed. If you
+are not using Eclipse, you can follow the exercises and build the application,
+but you will need to determine how to accomplish the Eclipse-specific
+steps in your environment. </p>
+
+<a name="preparing"></a>
+<h2>Preparing for the Exercises</h2>
+
+<p>This tutorial builds on the information provided in the <a
+href="{@docRoot}intro/installing.html">Installing the SDK</a> and <a
+href="{@docRoot}intro/hello-android.html">Hello Android</a>
+documents, which explain in detail how to set up your development environment
+for building Android applications. Before you start this tutorial, you should
+read both these documents, have the SDK installed, and your work environment set up.</p>
+
+<p>To prepare for this lesson:</p>
+
+<ol>
+ <li>Download the <a href="codelab/NotepadCodeLab.zip">project
+ exercises archive (.zip)</a></li>
+ <li>Unpack the archive file to a suitable location on your machine</li>
+ <li>Open the <code>NotepadCodeLab</code> folder</li>
+</ol>
+
+<p>Inside the <code>NotepadCodeLab</code> folder, you should see six project
+files: <code>Notepadv1</code>,
+ <code>Notepadv2</code>, <code>Notepadv3</code>,
+ <code>Notepadv1Solution</code>, <code>Notepadv2Solution</code>
+ and <code>Notepadv3Solution</code>. The <code>Notepadv#</code> projects are
+the starting points for each of the exercises, while the
+<code>Notepadv#Solution</code> projects are the exercise
+ solutions. If you are having trouble with a particular exercise, you
+ can compare your current work against the exercise solution.</p>
+
+<a name="exercises"></a>
+<h2> Exercises</h2>
+
+ <p>The table below lists the tutorial exercises and describes the development
+areas that each covers. Each exercise assumes that you have completed any
+previous exercises.</p>
+
+ <table border="0" style="padding:4px;spacing:2px;" summary="This
+table lists the
+tutorial examples and describes what each covers. ">
+ <tr>
+ <th width="120"><a href="{@docRoot}intro/tutorial-ex1.html">Exercise
+1</a></th>
+ <td>Start here. Construct a simple notes list that lets the user add new notes but not
+edit them. Demonstrates the basics of <code>ListActivity</code> and creating
+and handling
+ menu options. Uses a SQLite database to store the notes.</td>
+ </tr>
+ <tr>
+ <th><a href="{@docRoot}intro/tutorial-ex2.html">Exercise 2</a></th>
+ <td>Add a second Activity to the
+application. Demonstrates constructing a
+new Activity, adding it to the Android manifest, passing data between the
+activities, and using more advanced screen layout. Also shows how to
+invoke another Activity to return a result, using
+<code>startActivityForResult()</code>.</td>
+ </tr>
+ <tr>
+ <th><a href="{@docRoot}intro/tutorial-ex3.html">Exercise 3</a></th>
+ <td>Add handling of life-cycle events to
+the application, to let it
+maintain application state across the life cycle. </td>
+ </tr>
+ <tr>
+ <th><a href="{@docRoot}intro/tutorial-extra-credit.html">Extra
+Credit</a></th>
+ <td>Demonstrates how to use the Eclipse
+debugger and how you can use it to
+view life-cycle events as they are generated. This section is optional but
+highly recommended.</td>
+ </tr>
+</table>
+
+
+<a name="other"></a>
+<h2>Other Resources and Further Learning</h2>
+<ul>
+<li>For a lighter but broader introduction to concepts not covered in the
+tutorial,
+take a look at <a href="{@docRoot}kb/commontasks.html">Common Android Tasks</a>.</li>
+<li>The Android SDK includes a variety of fully functioning sample applications
+that make excellent opportunities for further learning. You can find the sample
+applications in the <code>samples/</code> directory of your downloaded SDK.</li>
+<li>This tutorial draws from the full Notepad application included in the
+<code>samples/</code> directory of the SDK, though it does not match it exactly.
+When you are done with the tutorial,
+it is highly recommended that you take a closer look at this version of the Notepad
+application,
+as it demonstrates a variety of interesting additions for your application,
+such as:</li>
+ <ul>
+ <li>Setting up a custom striped list for the list of notes.</li>
+ <li>Creating a custom text edit view that overrides the <code>draw()</code>
+method to
+ make it look like a lined notepad.</li>
+ <li>Implementing a full <code>ContentProvider</code> for notes.</li>
+ <li>Reverting and discarding edits instead of just automatically saving
+them.</li>
+</ul>
+</ul>
diff --git a/docs/html/resources/tutorials/views/hello-autocomplete.jd b/docs/html/resources/tutorials/views/hello-autocomplete.jd
new file mode 100644
index 0000000..fba1ad8
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-autocomplete.jd
@@ -0,0 +1,116 @@
+page.title=Hello, AutoCompleteTextView
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>{@link android.widget.AutoCompleteTextView} is an implementation of the EditText widget that will provide
+auto-complete suggestions as the user types. The suggestions are extracted from a collection of strings.</p>
+
+
+<ol>
+ <li>Start a new project/Activity called HelloAutoComplete.</li>
+ <li>Open the layout file.
+ Make it like so:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Country" />
+
+ <AutoCompleteTextView android:id="@+id/edit"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout>
+</pre>
+</li>
+
+<li>Open HelloAutoComplete.java and insert the following as the <code>onCreate</code> method:
+<pre>
+@Override
+protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.edit);
+ ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
+ android.R.layout.simple_dropdown_item_1line, COUNTRIES);
+ textView.setAdapter(adapter);
+}
+</pre>
+ <p>Here, we create an AutoComplteteTextView from our layout. We then
+ create an {@link android.widget.ArrayAdapter} that binds a <code>simple_dropdown_item_1line</code>
+ layout item to each entry in the <code>COUNTRIES</code> array (which we'll add next).
+ The last part sets the ArrayAdapter to associate with our AutoCompleteTextView.</p>
+</li>
+
+<li>After the <code>onCreate()</code> method, add the String array:
+<pre>
+static final String[] COUNTRIES = new String[] {
+ "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
+ "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
+ "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
+ "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
+ "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
+ "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory",
+ "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
+ "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
+ "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
+ "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
+ "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
+ "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
+ "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
+ "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
+ "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
+ "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
+ "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
+ "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
+ "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
+ "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
+ "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
+ "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
+ "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
+ "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
+ "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
+ "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
+ "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
+ "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
+ "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
+ "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
+ "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
+ "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
+ "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
+ "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
+ "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
+ "Ukraine", "United Arab Emirates", "United Kingdom",
+ "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
+ "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
+ "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
+};
+</pre>
+ <p>This is the list of suggestions that will be offered as the user types into the
+ AutoCompleteTextView.</p>
+</li>
+
+<li>Now run it.</li>
+</ol>
+<p>As you type, you should see something like this:</p>
+<img src="images/hello-autocomplete.png" width="150px" />
+
+
+<h3>References</h3>
+<ul>
+ <li>{@link android.R.layout}</li>
+ <li>{@link android.widget.ArrayAdapter}</li>
+ <li>{@link android.widget.AutoCompleteTextView}</li>
+</ul>
+
+
diff --git a/docs/html/resources/tutorials/views/hello-datepicker.jd b/docs/html/resources/tutorials/views/hello-datepicker.jd
new file mode 100644
index 0000000..fcd43f3
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-datepicker.jd
@@ -0,0 +1,151 @@
+page.title=Hello, DatePicker
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>A {@link android.widget.DatePicker} is a widget that allows the user to select a month, day and year.</p>
+
+
+<ol>
+ <li>Start a new project/Activity called HelloDatePicker.</li>
+ <li>Open the layout file and make it like so:
+ <pre>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView android:id="@+id/dateDisplay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""/>
+
+ <Button android:id="@+id/pickDate"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Change the date"/>
+
+</LinearLayout>
+</pre>
+ <p>For the layout, we're using a vertical LinearLayout, with a {@link android.widget.TextView} that
+ will display the date and a {@link android.widget.Button} that will initiate the DatePicker dialog.
+ With this layout, the TextView will sit above the Button.
+ The text value in the TextView is set empty, as it will be filled
+ with the current date when our Activity runs.</p>
+ </li>
+
+ <li>Open HelloDatePicker.java. Insert the following to the HelloDatePicker class:
+<pre>
+ private TextView mDateDisplay;
+ private Button mPickDate;
+
+ private int mYear;
+ private int mMonth;
+ private int mDay;
+
+ static final int DATE_DIALOG_ID = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ // capture our View elements
+ mDateDisplay = (TextView) findViewById(R.id.dateDisplay);
+ mPickDate = (Button) findViewById(R.id.pickDate);
+
+ // add a click listener to the button
+ mPickDate.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ showDialog(DATE_DIALOG_ID);
+ }
+ });
+
+ // get the current date
+ final Calendar c = Calendar.getInstance();
+ mYear = c.get(Calendar.YEAR);
+ mMonth = c.get(Calendar.MONTH);
+ mDay = c.get(Calendar.DAY_OF_MONTH);
+
+ // display the current date
+ updateDisplay();
+ }
+</pre>
+<p class="note"><strong>Tip:</strong> Press Ctrl(or Cmd) + Shift + O to import all needed packages.</p>
+ <p>We start by instantiating variables for our Views and date fields.
+ The <code>DATE_DIALOG_ID</code> is a static integer that uniquely identifies the Dialog. In the
+ <code>onCreate()</code> method, we get prepared by setting the layout and capturing the View elements.
+ Then we create an on-click listener for the Button, so that when it is clicked it will
+ show our DatePicker dialog. The <code>showDialog()</code> method will pop-up the date picker dialog
+ by calling the <code>onCreateDialog()</code> callback method
+ (which we'll define in the next section). We then create an
+ instance of {@link java.util.Calendar} and get the current year, month and day. Finally, we call
+ <code>updateDisplay()</code>—our own method (defined later) that will fill the TextView.</p>
+</li>
+
+<li>After the <code>onCreate()</code> method, add the <code>onCreateDialog()</code> callback method
+(which is called by <code>showDialog()</code>)
+<pre>
+@Override
+protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case DATE_DIALOG_ID:
+ return new DatePickerDialog(this,
+ mDateSetListener,
+ mYear, mMonth, mDay);
+ }
+ return null;
+}
+</pre>
+ <p>This method is passed the identifier we gave <code>showDialog()</code> and initializes
+ the DatePicker to the date we retrieved from our Calendar instance.</p>
+</li>
+
+<li>Following that, add the <code>updateDisplay()</code> method:
+<pre>
+ // updates the date we display in the TextView
+ private void updateDisplay() {
+ mDateDisplay.setText(
+ new StringBuilder()
+ // Month is 0 based so add 1
+ .append(mMonth + 1).append("-")
+ .append(mDay).append("-")
+ .append(mYear).append(" "));
+ }
+</pre>
+<p>This uses the member date values to write the date to our TextView.</p>
+</li>
+<li>Finally, add a listener that will be called when the user sets a new date:
+<pre>
+ // the callback received when the user "sets" the date in the dialog
+ private DatePickerDialog.OnDateSetListener mDateSetListener =
+ new DatePickerDialog.OnDateSetListener() {
+
+ public void onDateSet(DatePicker view, int year,
+ int monthOfYear, int dayOfMonth) {
+ mYear = year;
+ mMonth = monthOfYear;
+ mDay = dayOfMonth;
+ updateDisplay();
+ }
+ };
+</pre>
+ <p>This <code>OnDateSetListener</code> method listens for when the user is done setting the date
+ (clicks the "Set" button). At that time, this fires and we update our member fields with
+ the new date defined by the user and update our TextView by calling <code>updateDisplay()</code>.</p>
+</li>
+
+<li>Now run it.</li>
+</ol>
+<p>When you press the "Change the date" button, you should see the following:</p>
+<img src="images/hello-datepicker.png" width="150px" />
+
+<h3>References</h3>
+<ul>
+<li>{@link android.widget.DatePicker}</li>
+<li>{@link android.widget.Button}</li>
+<li>{@link android.widget.TextView}</li>
+<li>{@link java.util.Calendar}</li>
+</ul>
+
diff --git a/docs/html/resources/tutorials/views/hello-formstuff.jd b/docs/html/resources/tutorials/views/hello-formstuff.jd
new file mode 100644
index 0000000..da4289c
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-formstuff.jd
@@ -0,0 +1,262 @@
+page.title=Hello, Form Stuff
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>This page introduces a variety of widgets, like image buttons,
+text fields, checkboxes and radio buttons.</p>
+
+
+<ol>
+ <li>Start a new project/Activity called HelloFormStuff.</li>
+ <li>Your layout file should have a basic LinearLayout:
+ <pre>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+</LinearLayout>
+</pre>
+ <p>For each widget you want to add, just put the respective View inside here.</p>
+</li>
+</ol>
+<p class="note"><strong>Tip:</strong> As you add new Android code, press Ctrl(or Cmd) + Shift + O
+to import all needed packages.</p>
+
+
+<h2>ImageButton</h2>
+<p>A button with a custom image on it.
+We'll make it display a message when pressed.</p>
+<ol>
+ <li><img src="images/android.png" align="right"/>
+ Drag the Android image on the right (or your own image) into the
+ res/drawable/ directory of your project.
+ We'll use this for the button.</li>
+ <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.ImageButton} element:
+<pre>
+<ImageButton
+ android:id="@+id/android_button"
+ android:layout_width="100dip"
+ android:layout_height="wrap_content"
+ android:src="@drawable/android" />
+</pre>
+ <p>The source of the button
+ is from the res/drawable/ directory, where we've placed the android.png.</p>
+ <p class="note"><strong>Tip:</strong> You can also reference some of the many built-in
+ images from the Android {@link android.R.drawable} resources,
+ like <code>ic_media_play</code>, for a "play" button image. To do so, change the source
+ attribute to <code>android:src="@android:drawable/ic_media_play"</code>.</p>
+</li>
+<li>To make the button to actually do something, add the following
+code at the end of the <code>onCreate()</code> method:
+<pre>
+final ImageButton button = (ImageButton) findViewById(R.id.android_button);
+button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ // Perform action on clicks
+ Toast.makeText(HelloFormStuff.this, "Beep Bop", Toast.LENGTH_SHORT).show();
+ }
+});
+</pre>
+<p>This captures our ImageButton from the layout, then adds an on-click listener to it.
+The {@link android.view.View.OnClickListener} must define the <code>onClick()</code> method, which
+defines the action to be made when the button is clicked. Here, we show a
+{@link android.widget.Toast} message when clicked.</p>
+</li>
+<li>Run it.</li>
+</ol>
+
+
+<h2>EditText</h2>
+<p>A text field for user input. We'll make it display the text entered so far when the "Enter" key is pressed.</p>
+
+<ol>
+ <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.EditText} element:
+<pre>
+<EditText
+ android:id="@+id/edittext"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+</pre>
+</li>
+<li>To do something with the text that the user enters, add the following code
+to the end of the <code>onCreate()</code> method:
+<pre>
+final EditText edittext = (EditText) findViewById(R.id.edittext);
+edittext.setOnKeyListener(new OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
+ // Perform action on key press
+ Toast.makeText(HelloFormStuff.this, edittext.getText(), Toast.LENGTH_SHORT).show();
+ return true;
+ }
+ return false;
+ }
+});
+</pre>
+<p>This captures our EditText element from the layout, then adds an on-key listener to it.
+The {@link android.view.View.OnKeyListener} must define the <code>onKey()</code> method, which
+defines the action to be made when a key is pressed. In this case, we want to listen for the
+Enter key (when pressed down), then pop up a {@link android.widget.Toast} message with the
+text from the EditText field. Be sure to return <var>true</var> after the event is handled,
+so that the event doesn't bubble-up and get handled by the View (which would result in a
+carriage return in the text field).</p>
+<li>Run it.</li>
+</ol>
+
+
+<h2>CheckBox</h2>
+<p>A checkbox for selecting items. We'll make it display the the current state when pressed.</p>
+
+<ol>
+ <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.CheckBox} element:
+<pre>
+<CheckBox android:id="@+id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="check it out" />
+</pre>
+</li>
+<li>To do something when the state is changed, add the following code
+to the end of the <code>onCreate()</code> method:
+<pre>
+final CheckBox checkbox = (CheckBox) findViewById(R.id.checkbox);
+checkbox.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ // Perform action on clicks
+ if (checkbox.isChecked()) {
+ Toast.makeText(HelloFormStuff.this, "Selected", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(HelloFormStuff.this, "Not selected", Toast.LENGTH_SHORT).show();
+ }
+ }
+});
+</pre>
+<p>This captures our CheckBox element from the layout, then adds an on-click listener to it.
+The {@link android.view.View.OnClickListener} must define the <code>onClick()</code> method, which
+defines the action to be made when the checkbox is clicked. Here, we query the current state of the
+checkbox, then pop up a {@link android.widget.Toast} message that displays the current state.
+Notice that the CheckBox handles its own state change between checked and un-checked, so we just
+ask which it currently is.</p>
+<li>Run it.</li>
+</ol>
+<p class="note"><strong>Tip:</strong> If you find that you need to change the state
+in another way (such as when loading a saved {@link android.preference.CheckBoxPreference}),
+use <code>setChecked(true)</code> or <code>toggle()</code>.</p>
+
+
+<h2>RadioButton</h2>
+<p>Two mutually-exclusive radio buttons—enabling one disables the other.
+When each is pressed, we'll pop up a message.</p>
+
+<ol>
+ <li>Open the layout file and, inside the LinearLayout, add two {@link android.widget.RadioButton}s,
+inside a {@link android.widget.RadioGroup}:
+<pre>
+<RadioGroup
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <RadioButton android:id="@+id/radio_red"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Red" />
+
+ <RadioButton android:id="@+id/radio_blue"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Blue" />
+
+</RadioGroup>
+</pre>
+</li>
+<li>To do something when each is selected, we'll need an OnClickListener. Unlike the other
+listeners we've created, instead of creating this one as an anonymous inner class,
+we'll create it as a new object. This way, we can re-use the OnClickLIstener for
+both RadioButtons. So, add the following code in the HelloFormStuff Activity
+(<em>outside</em> the <code>onCreate()</code> method):
+<pre>
+OnClickListener radio_listener = new OnClickListener() {
+ public void onClick(View v) {
+ // Perform action on clicks
+ RadioButton rb = (RadioButton) v;
+ Toast.makeText(HelloFormStuff.this, rb.getText(), Toast.LENGTH_SHORT).show();
+ }
+};
+</pre>
+<p>Our <code>onClick()</code> method will be handed the View clicked, so the first thing to do
+is cast it into a RadioButton. Then we pop up a
+{@link android.widget.Toast} message that displays the selection.</p>
+<li>Now, at the bottom of the <code>onCreate()</code> method, add the following:
+<pre>
+ final RadioButton radio_red = (RadioButton) findViewById(R.id.radio_red);
+ final RadioButton radio_blue = (RadioButton) findViewById(R.id.radio_blue);
+ radio_red.setOnClickListener(radio_listener);
+ radio_blue.setOnClickListener(radio_listener);
+</pre>
+<p>This captures each of the RadioButtons from our layout and adds the newly-created
+OnClickListener to each.</p>
+<li>Run it.</li>
+</ol>
+<p class="note"><strong>Tip:</strong> If you find that you need to change the state of a
+RadioButton in another way (such as when loading a saved {@link android.preference.CheckBoxPreference}),
+use <code>setChecked(true)</code> or <code>toggle()</code>.</p>
+
+
+<h2>ToggleButton</h2>
+<p>A button used specifically for toggling something on and off.</p>
+
+<ol>
+ <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.ToggleButton} element:
+<pre>
+<ToggleButton android:id="@+id/togglebutton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+</pre>
+</li>
+<li>To do something when the state is changed, add the following code
+to the end of the <code>onCreate()</code> method:
+<pre>
+final ToggleButton togglebutton = (ToggleButton) findViewById(R.id.togglebutton);
+togglebutton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ // Perform action on clicks
+ if (togglebutton.isChecked()) {
+ Toast.makeText(HelloFormStuff.this, "ON", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(HelloFormStuff.this, "OFF", Toast.LENGTH_SHORT).show();
+ }
+ }
+});
+</pre>
+<p>This captures our ToggleButton element from the layout, then adds an on-click listener to it.
+The {@link android.view.View.OnClickListener} must define the <code>onClick()</code> method, which
+defines the action to be made when the button is clicked. Here, we query the current state of the
+ToggleButton, then pop up a {@link android.widget.Toast} message that displays the current state.
+Notice that the ToggleButton handles its own state change between checked and un-checked, so we just
+ask which it is.</p>
+<li>Run it.</li>
+</ol>
+
+<p class="note"><strong>Tip:</strong> By default, the text on the button is "ON" and "OFF", but
+you can change each of these with <code>setTextOn(<var>CharSequence</var>)</code> and
+<code>setTextOff(<var>CharSequence</var>)</code>. And, if you find that you need to change the state
+in another way (such as when loading a saved {@link android.preference.CheckBoxPreference}),
+use <code>setChecked(true)</code> or <code>toggle()</code>. </p>
+
+
+<p>If you've added all the form items above, your application should look something like this:</p>
+<img src="images/hello-formstuff.png" width="150px" />
+
+<h3>References</h3>
+<ul>
+ <li>{@link android.widget.ImageButton}</li>
+ <li>{@link android.widget.EditText}</li>
+ <li>{@link android.widget.CheckBox}</li>
+ <li>{@link android.widget.RadioButton}</li>
+ <li>{@link android.widget.ToggleButton}</li>
+</ul>
+
diff --git a/docs/html/resources/tutorials/views/hello-gallery.jd b/docs/html/resources/tutorials/views/hello-gallery.jd
new file mode 100644
index 0000000..084f912
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-gallery.jd
@@ -0,0 +1,135 @@
+page.title=Hello, Gallery
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>A {@link android.widget.Gallery} is a View commonly used to display items in a horizontally scrolling list
+that locks the current selection at the center. When one is selected, we'll show a message.</p>
+
+
+<ol>
+ <li>Start a new project/Activity called HelloGallery.</li>
+ <li>Add some images to your res/drawable/ directory.</li>
+ <li>Open the layout file and make it like so:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<Gallery xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/gallery"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+/>
+</pre>
+</li>
+
+
+<li>Open the HelloGallery.java file. Insert the following for the <code>onCreate()</code> method:
+<pre>
+@Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ Gallery g = (Gallery) findViewById(R.id.gallery);
+ g.setAdapter(new ImageAdapter(this));
+
+ g.setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView parent, View v, int position, long id) {
+ Toast.makeText(HelloGallery.this, "" + position, Toast.LENGTH_SHORT).show();
+ }
+ });
+}
+</pre>
+ <p>We start as usual: set the layout and capture the View we want (our Gallery).
+We then set an Adapter, called ImageAdapter for the Gallery—this is a new class that
+we'll create next. Then we create an item click listener for the Gallery. This is like a normal
+on-click listener (which you might be familiar with for buttons), but it listens to each item
+that we've added to the Gallery. The <code>onItemClick()</code> callback method
+receives the AdapterView where the click occurred, the specific View that received the click, the
+position of the View clicked (zero-based), and the row id of the item clicked (if applicable). All
+that we care about is the position, so that we can pop up a {@link android.widget.Toast} message that
+tells us the index position of the item clicked. We do this with <code>Toast.makeText().show()</code>.
+</p>
+</li>
+
+<li>After the <code>onCreate()</code> method, add the <code>ImageAdapter</code> class:
+<pre>
+public class ImageAdapter extends BaseAdapter {
+ int mGalleryItemBackground;
+ private Context mContext;
+
+ private Integer[] mImageIds = {
+ R.drawable.sample_1,
+ R.drawable.sample_2,
+ R.drawable.sample_3,
+ R.drawable.sample_4,
+ R.drawable.sample_5,
+ R.drawable.sample_6,
+ R.drawable.sample_7
+ };
+
+ public ImageAdapter(Context c) {
+ mContext = c;
+ TypedArray a = obtainStyledAttributes(android.R.styleable.Theme);
+ mGalleryItemBackground = a.getResourceId(
+ android.R.styleable.Theme_galleryItemBackground, 0);
+ a.recycle();
+ }
+
+ public int getCount() {
+ return mImageIds.length;
+ }
+
+ public Object getItem(int position) {
+ return position;
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ImageView i = new ImageView(mContext);
+
+ i.setImageResource(mImageIds[position]);
+ i.setLayoutParams(new Gallery.LayoutParams(150, 100));
+ i.setScaleType(ImageView.ScaleType.FIT_XY);
+ i.setBackgroundResource(mGalleryItemBackground);
+
+ return i;
+ }
+}
+</pre>
+<p>First, there are a few member variables, including an array of IDs that reference
+the images we placed in our drawable resources directory.</p>
+<p>Next is the constructor, where we define the member Context. The rest of the constructor
+sets up a reference for our Gallery them, which adds the nice framing for each Gallery item.
+Once we have our <code>mGalleryItemBackground</code>, it's important to recycle the
+StyledAttribute for later re-use.</p>
+<p>The next three methods are required for basic member queries.
+But then we have the <code>getView()</code> method, which is called
+for each item read by our ImageAdapter, when the Gallery is being built. Here, we
+use our member Context to create a new {@link android.widget.ImageView}. We then define
+the image resource with the current position of the Gallery items (corresponding to our
+array of drawables), set the dimensions for the ImageView,
+set the image scaling to fit the ImageView dimensions, then finally set the
+background theme for the ImageView.</p>
+
+<p>See {@link android.widget.ImageView.ScaleType}
+for other image scaling options, in case you want to avoid stretching images that don't
+exactly match the ImageView dimensions.</p>
+
+<li>Now run it.</li>
+</ol>
+<p>You should see something like this:</p>
+<img src="images/hello-gallery.png" width="150px" />
+
+
+<h3>References</h3>
+<ul>
+ <li>{@link android.widget.BaseAdapter}</li>
+ <li>{@link android.widget.Gallery}</li>
+ <li>{@link android.widget.ImageView}</li>
+ <li>{@link android.widget.Toast}</li>
+</ul>
+
+
diff --git a/docs/html/resources/tutorials/views/hello-gridview.jd b/docs/html/resources/tutorials/views/hello-gridview.jd
new file mode 100644
index 0000000..ffb6c93
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-gridview.jd
@@ -0,0 +1,129 @@
+page.title=Hello, GridView
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>A {@link android.widget.GridView} displays items in a two-dimensional, scrolling grid. The items
+are acquired from a {@link android.widget.ListAdapter}.</p>
+
+
+<ol>
+ <li>Start a new project/Activity called HelloGridView.</li>
+ <li>Find some photos you'd like to use, or copy some from the SDK samples res/drawable/
+ folder of your project.</li>
+ <li>Open the layout and make it like so:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<GridView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/gridview"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:numColumns="auto_fit"
+ android:verticalSpacing="10dp"
+ android:horizontalSpacing="10dp"
+ android:columnWidth="90dp"
+ android:stretchMode="columnWidth"
+ android:gravity="center"
+/>
+</pre>
+</li>
+ <li>Open the HelloGridView Java file. Insert the following for the <code>onCreate()</code> method:
+<pre>
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ GridView gridview = (GridView) findViewById(R.id.gridview);
+ gridview.setAdapter(new ImageAdapter(this));
+}
+</pre>
+ <p>Here, we get a handle on our GridView, from the layout, and give it an Adapter.
+ We're actually going to create our own Adapter called ImageAdapter.</p>
+</li>
+<li>Create a new class (nested or otherwise), called ImageAdapter, which extends {@link android.widget.BaseAdapter}:
+<pre>
+public class ImageAdapter extends BaseAdapter {
+ private Context mContext;
+
+ public ImageAdapter(Context c) {
+ mContext = c;
+ }
+
+ public int getCount() {
+ return mThumbIds.length;
+ }
+
+ public Object getItem(int position) {
+ return null;
+ }
+
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ // create a new ImageView for each item referenced by the Adapter
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ImageView imageView;
+ if (convertView == null) { // if it's not recycled, initialize some attributes
+ imageView = new ImageView(mContext);
+ imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
+ imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ imageView.setPadding(8, 8, 8, 8);
+ } else {
+ imageView = (ImageView) convertView;
+ }
+
+ imageView.setImageResource(mThumbIds[position]);
+ return imageView;
+ }
+
+ // references to our images
+ private Integer[] mThumbIds = {
+ R.drawable.sample_2, R.drawable.sample_3,
+ R.drawable.sample_4, R.drawable.sample_5,
+ R.drawable.sample_6, R.drawable.sample_7,
+ R.drawable.sample_0, R.drawable.sample_1,
+ R.drawable.sample_2, R.drawable.sample_3,
+ R.drawable.sample_4, R.drawable.sample_5,
+ R.drawable.sample_6, R.drawable.sample_7,
+ R.drawable.sample_0, R.drawable.sample_1,
+ R.drawable.sample_2, R.drawable.sample_3,
+ R.drawable.sample_4, R.drawable.sample_5,
+ R.drawable.sample_6, R.drawable.sample_7
+ };
+}
+</pre>
+ <p>First we take care of some required methods inherited from BaseAdapter.
+ The constructor and <code>getCount()</code> are self-explanitory. Normally, <code>getItem()</code>
+ should return the actual object at the specified position in our Adapter, but for this Hello World,
+ we're not going to bother. Likewise, <code>getItemId()</code> should return the row id of
+ the item, but right now we don't care.</p>
+ <p>However, <code>getView()</code> is the method we care about. This one creates a new View for each image that we
+ put in our ImageAdapter. So we're going to create an ImageView each time. When this is called, we're
+ going to receive a View, which is likely a recycled View object (at least after the first call), so we
+ check for this—if it's null, we initialize the ImageView and setup all the properties we want.
+ The <code>LayoutParams()</code> initialization sets the height and width of the View—this ensures
+ that no matter the drawable size, each image is resized and cropped to fit in the ImageView (if necessary).
+ With <code>setScaleType()</code>, we say that images should be cropped toward the center (if necessary).
+ And finally, we set the padding within the ImageView. (Note that, if the images have various aspect-ratios,
+ as they do in this demo, then less padding will cause for more cropping of the image, if it does not match
+ the dimensions given to the ImageView.) At the end of <code>getView()</code> we set the image resource and
+ return the ImageView.</p>
+ <p>All that's left is our array or drawable resources at the bottom.</p>
+</li>
+<li>Run it.</li>
+</ol>
+<p>Your grid layout should look something like this:</p>
+<img src="images/hello-gridview.png" width="150px" />
+
+<p>Try experimenting with the behaviors of the GridView and ImageView by adjusting their properties. For example,
+ instead of setting the ImageView LayoutParams, you can try using
+ {@link android.widget.ImageView#setAdjustViewBounds(boolean)}. </p>
+
+<h3>References</h3>
+<ul>
+ <li>{@link android.widget.GridView}</li>
+ <li>{@link android.widget.ImageView}</li>
+ <li>{@link android.widget.BaseAdapter}</li>
+</ul>
+
diff --git a/docs/html/resources/tutorials/views/hello-linearlayout.jd b/docs/html/resources/tutorials/views/hello-linearlayout.jd
new file mode 100644
index 0000000..0e8947c
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-linearlayout.jd
@@ -0,0 +1,130 @@
+page.title=Hello, LinearLayout
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>A {@link android.widget.LinearLayout} is a GroupView that will lay child View elements
+vertically or horizontally.</p>
+
+
+<ol>
+ <li>Start a new project/Activity called HelloLinearLayout.</li>
+ <li>Open the layout file.
+ Make it like so:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1">
+
+ <TextView
+ android:text="red"
+ android:gravity="center_horizontal"
+ android:background="#aa0000"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:text="green"
+ android:gravity="center_horizontal"
+ android:background="#00aa00"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:text="blue"
+ android:gravity="center_horizontal"
+ android:background="#0000aa"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:text="yellow"
+ android:gravity="center_horizontal"
+ android:background="#aaaa00"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1">
+
+ <TextView
+ android:text="row one"
+ android:textSize="15pt"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:text="row two"
+ android:textSize="15pt"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:text="row three"
+ android:textSize="15pt"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <TextView
+ android:text="row four"
+ android:textSize="15pt"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ </LinearLayout>
+
+</LinearLayout>
+</pre>
+ <p>Carefully inspect the XML. You'll notice how this layout works a lot like
+ an HTML layout. There is one parent LinearLayout that is defined to lay
+ its child elements vertically. The first child is another LinearLayout that uses a horizontal layout
+ and the second uses a vertical layout. Each LinearLayout contains several {@link android.widget.TextView}
+ elements.</p>
+</li>
+<li>Now open the HelloLinearLayout Activity and be sure it loads this layout in the <code>onCreate()</code> method:</p>
+<pre>
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+}
+</pre>
+<p><code>R.layout.main</code> refers to the <code>main.xml</code> layout file.</p>
+</li>
+<li>Run it.</li>
+</ol>
+<p>You should see the following:</p>
+<img src="images/hello-linearlayout.png" width="150px" />
+
+<p>Notice how the various XML attributes define the View's behavior.
+Pay attention to the effect of the <code>layout_weight</code>. Try
+ experimenting with different values to see how the screen real estate is
+ distributed based on the weight of each element.</p>
+
+<h3>References</h3>
+<ul>
+ <li>{@link android.widget.LinearLayout}</li>
+<li>{@link android.widget.TextView}</li>
+</ul>
+
+
diff --git a/docs/html/resources/tutorials/views/hello-listview.jd b/docs/html/resources/tutorials/views/hello-listview.jd
new file mode 100644
index 0000000..d90005b
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-listview.jd
@@ -0,0 +1,90 @@
+page.title=Hello, ListView
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>A {@link android.widget.ListView} is a View that shows items in a vertically scrolling list. The items are
+ acquired from a {@link android.widget.ListAdapter}.</p>
+
+
+<ol>
+ <li>Start a new project/ListActivity called HelloListView.</li>
+ <li>Open the HelloListView Java file. Make the class extend ListActivity (instead of Activity).
+ <pre>public class HelloListView extends ListActivity {</pre>
+ </li>
+ <li>Insert the following for the <code>onCreate()</code> method:
+<pre>
+@Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setListAdapter(new ArrayAdapter<String>(this,
+ android.R.layout.simple_list_item_1, COUNTRIES));
+ getListView().setTextFilterEnabled(true);
+}
+</pre>
+ <p>Notice that we don't need to load a layout (at least, not in this case, because we're using
+ the whole screen for our list). Instead, we just call <code>setListAdapter()</code> (which automatically
+ adds a ListView to the ListActivity), and provide it with an ArrayAdapter that binds a
+ <code>simple_list_item_1</code> layout item to each entry in the <code>COUNTRIES</code>
+ array (added next). The next line of code adds a text filter to the ListView, so that when the user
+ begins typing, the list will filter the entire view to display only the items that match the entry.</p>
+ </li>
+ <li>Following the <code>onCreate()</code> method, add the String array:
+<pre>
+ static final String[] COUNTRIES = new String[] {
+ "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
+ "Angola", "Anguilla", "Antarctica", "Antigua and Barbuda", "Argentina",
+ "Armenia", "Aruba", "Australia", "Austria", "Azerbaijan",
+ "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
+ "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia",
+ "Bosnia and Herzegovina", "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory",
+ "British Virgin Islands", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
+ "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
+ "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
+ "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
+ "East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
+ "Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
+ "Former Yugoslav Republic of Macedonia", "France", "French Guiana", "French Polynesia",
+ "French Southern Territories", "Gabon", "Georgia", "Germany", "Ghana", "Gibraltar",
+ "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
+ "Guyana", "Haiti", "Heard Island and McDonald Islands", "Honduras", "Hong Kong", "Hungary",
+ "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
+ "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos",
+ "Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
+ "Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
+ "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
+ "Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia",
+ "Nauru", "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand",
+ "Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island", "North Korea", "Northern Marianas",
+ "Norway", "Oman", "Pakistan", "Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
+ "Philippines", "Pitcairn Islands", "Poland", "Portugal", "Puerto Rico", "Qatar",
+ "Reunion", "Romania", "Russia", "Rwanda", "Sqo Tome and Principe", "Saint Helena",
+ "Saint Kitts and Nevis", "Saint Lucia", "Saint Pierre and Miquelon",
+ "Saint Vincent and the Grenadines", "Samoa", "San Marino", "Saudi Arabia", "Senegal",
+ "Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
+ "Somalia", "South Africa", "South Georgia and the South Sandwich Islands", "South Korea",
+ "Spain", "Sri Lanka", "Sudan", "Suriname", "Svalbard and Jan Mayen", "Swaziland", "Sweden",
+ "Switzerland", "Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand", "The Bahamas",
+ "The Gambia", "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey",
+ "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
+ "Ukraine", "United Arab Emirates", "United Kingdom",
+ "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan",
+ "Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Wallis and Futuna", "Western Sahara",
+ "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
+ };
+</pre>
+</li>
+<li> Run it.</li>
+</ol>
+<p>You can scroll the list, or type to filter it. You should see something like this:</p>
+<img src="images/hello-listview.png" width="150px" />
+
+<h3>References</h3>
+<ul>
+ <li>{@link android.widget.ListView}</li>
+ <li>{@link android.widget.ListAdapter}</li>
+</ul>
+
diff --git a/docs/html/resources/tutorials/views/hello-mapview.jd b/docs/html/resources/tutorials/views/hello-mapview.jd
new file mode 100644
index 0000000..531300f
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-mapview.jd
@@ -0,0 +1,273 @@
+page.title=Hello, MapView
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<div class="special">
+<p>This tutorial requires that you have the Google Maps external library
+installed in your SDK environment. By default the Android SDK includes the
+Google APIs add-on, which in turn includes the Maps external library. If you
+don't have the Google APIs SDK add-on, you can download it from this
+location:</p>
+
+<p style="margin-left:2em;"><a
+href="http://code.google.com/android/add-ons/google-apis">http://code.google.com/android/add-ons/google-apis</a></p>
+
+<p>The Google APIs add-on requires Android 1.5 SDK or later release. After
+installing the add-on in your SDK, set your project properties to use the build
+target called "Google APIs Add-on". See the instructions for setting a build
+target in <a href="{@docRoot}guide/developing/eclipse-adt.html">Developing in
+Eclipse with ADT</a> or <a
+href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>,
+as appropriate for your environment. </p>
+
+<p>You will also need to use the android tool to set up an AVD that uses the
+Google APIs deployment target. See <a
+href="{@docRoot}guide/developing/tools/avd.html">Android Virtual Devices</a> for
+more information. Once you have set up your environment, you will be able to
+build and run the project described in this tutorial</a></p>
+
+</div>
+
+<p>A MapView allows you to create your own map-viewing Activity.
+First, we'll create a simple Activity that can view and navigate a map. Then we will add some overlay items.</p>
+
+<ol>
+ <li>Start a new project/Activity called HelloMapView.
+
+ <li>Because we're using the Google Maps library,
+ which is not a part of the standard Android library, we need to
+ declare it in the Android Manifest. Open the AndroidManifest.xml
+ file and add the following as a child of the <code><application></code> element:
+
+ <pre><uses-library android:name="com.google.android.maps" /></pre>
+ </li>
+ <li>We also need access to the internet in order to retrieve the Google Maps tiles,
+ so the application must request the {@link android.Manifest.permission#INTERNET INTERNET} permissions.
+ In the manifest file, add the following as a child of the <code><manifest></code> element:
+ <pre><uses-permission android:name="android.permission.INTERNET" /></pre>
+ </li>
+ <li>Now open the main layout file for your project. Define a layout with a com.google.android.maps.MapView
+ inside a android.widget.RelativeLayout:
+
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/mainlayout"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
+
+ <com.google.android.maps.MapView
+ android:id="@+id/mapview"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:clickable="true"
+ android:apiKey="<em>Your Maps API Key</em>"
+ />
+
+</RelativeLayout>
+</pre>
+ <p>The <code>clickable</code> attribute defines whether you want to allow user-interaction with the map.
+ In this case, we set it "true" so that the user can navigate.</p>
+
+ <p>The <code>apiKey</code> attribute holds the Google Maps API Key that proves your application and signer
+ certificate has been registered with the Google Maps service. Because MapView uses Google Maps data, this key is required
+ in order to receive the map data, even while you are developing. Registration is free and it only takes a couple
+ minutes to register your certificate and receive a Maps API Key. For instructions on getting a key, read
+ <a href="http://code.google.com/android/add-ons/google-apis/mapkey.html">Obtaining a Maps API Key</a>.
+ (For the purpose of this tutorial, you should register with the fingerprint of the SDK debug certificate.)
+ Once you've acquired the Maps API Key, insert it for the <code>apiKey</code> value.</p></li>
+
+ <li>Now open the HelloMapView.java file. For this Activity, we're going to extend the special sub-class of
+ Activity called MapActivity, so change the class declaration to extend
+ MapActicity, instead of Activity:</p>
+
+ <pre>public class HelloMapView extends MapActivity {</pre>
+
+ <li>The <code>isRouteDisplayed()</code> method is required, so add it inside the class:
+<pre>
+@Override
+protected boolean isRouteDisplayed() {
+ return false;
+}
+</pre>
+<p>You can actually run this now, but all it does is allow you to pan around the map.</p>
+<p>Android provides a handy {@link android.widget.ZoomControls} widget for zooming in and out of a View.
+MapView can automatically hook one for us by requesting it with the <code>getZoomControls()</code>
+method. Let's do this.</p>
+
+<li>Go back to the layout file. We need a new ViewGroup element, in which we'll
+ place the ZoomControls. Just below the MapView element (but inside the RelativeLayout), add this element:
+<pre>
+<LinearLayout
+ android:id="@+id/zoomview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@id/mapview"
+ android:layout_centerHorizontal="true"
+/></pre>
+
+ <p>It doesn't really matter what kind of ViewGroup we use, because we just want a
+ container that we can position within our root RelativeLayout.</p>
+
+ <p>The last two attributes are available only to an element that's a child of a
+ RelativeLayout. <code>layout_alignBottom</code> aligns the bottom of this element to the bottom of
+ the element identified with a resource tag (which must be a sibling to this element).
+ <code>layout_centerHorizontal</code> centers this on the horizontal plane.</p></li>
+
+ <li>Now go back to the HelloMapView class. We'll now retrieve the ZoomControls object from
+ the MapView and add it to our new layout element. First, at the top of the HelloMapView,
+ instantiate handles for the MapView and LinearLayout, plus a ZoomControl object:
+<pre>
+LinearLayout linearLayout;
+MapView mapView;
+ZoomControls mZoom;</pre></li>
+
+ <li>Then initialize each of these in <code>onCreate()</code>. We'll capture the LinearLayout and
+ MapView through their layout resources. Then get the ZoomControls from the MapView::
+<pre>
+linearLayout = (LinearLayout) findViewById(R.id.zoomview);
+mapView = (MapView) findViewById(R.id.mapview);
+mZoom = (ZoomControls) mapView.getZoomControls();</pre>
+
+ <p>By using the ZoomControls object provided by MapView, we don't have to do any of the work
+ required to actually perform the zoom operations. The ZoomControls widget that MapView
+ returns for us is already hooked into the MapView and works as soon as we add it to the
+ layout. The controls will appear whenever the user touches the map, then dissapear after
+ a few moments of inactivity.</p></li>
+
+ <li>Now just plug our ZoomControls into the LinearLayout we added:
+
+ <pre>linearLayout.addView(mZoom);</pre></li>
+
+ <li>Run it.</li>
+</ol>
+
+<hr/>
+
+<p>So, we now have full interaction controls. All well and good, but what we really want our map
+for is custom markers and layovers. Let's add some Overlay
+objects to our map. To do this, we're going to
+implement the ItemizedOverlay
+class, which can manage a whole set of Overlay items for us.</p>
+
+<ol>
+ <li>Create a new Java class named HelloItemizedOverlay that implements ItemizedOverlay.
+
+ <p>When using Eclipse, right-click the package name in the Eclipse Package Explorer, and select New > Class. Fill-in
+ the Name field as <em>HelloItemizedOverlay</em>. For the Superclass, enter
+ <em>com.google.android.maps.ItemizedOverlay</em>. Click the checkbox for <em>Constructors from
+ superclass</em>. Click Finish.</p></li>
+
+ <li> First thing, we need an OverlayItem ArrayList, in which we'll put each of the OverlayItem
+ objects we want on our map. Add this at the top of the HelloItemizedOverlay class:
+
+ <pre>private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();</pre></li>
+
+ <li>All the constructor does is define the default marker to be used on each of the OverlayItems.
+ In order for the Drawable to actually get drawn, it must have its bounds defined. And we want the
+ center-point at the bottom of the image to be the point at which it's attached to the map
+ coordinates. We handle all this with the boundCenterBottom() method. Wrap this around our
+ defaultMarker, so the super constructor call looks like this:
+
+ <pre>super(boundCenterBottom(defaultMarker));</pre></li>
+
+ <li>In order to add new OverlayItems to our ArrayList, we need a new public method. We'll handle
+ this with the following method:
+
+<pre>
+public void addOverlay(OverlayItem overlay) {
+ mOverlays.add(overlay);
+ populate();
+}</pre>
+
+ <p>Each time we add a new OverlayItem, we must call <code>populate()</code>, which will read each of out
+ OverlayItems and prepare them to be drawn.</p></li>
+
+ <li>In order for the <code>populate()</code> method to read each OverlayItem, it will make a request to
+ <code>createItem(int)</code>. We must define this method to properly read from our ArrayList. Replace the
+ existing contents of the createItem method with a <code>get()</code> call to our ArrayList:
+
+<pre>
+@Override
+protected OverlayItem createItem(int i) {
+ return mOverlays.get(i);
+}
+</pre></li>
+
+ <li>We're also required to override the <code>size()</code> method. Replace the existing contents of the
+ method with a size request to our ArrayList:
+
+ <pre>return mOverlays.size();</pre></li>
+</ol>
+
+
+<p>That's it for the HelloItemizedOverlay class. We're now ready to use it.</p>
+
+<hr/>
+<p>Go back to the HelloMapView
+class. We'll start by creating one OverlayItem, adding to an instance of our HelloItemizedOverlay,
+and then adding this to the MapView.</p>
+
+<img src="images/androidmarker.png" align="right" />
+<p>First, we need the image that we'll use for our map overlay. Here, we'll use the Android on the
+right as our marker. Drag this image (or your own) to the res/drawable/ directory of your project workspace.</p>
+
+<p>Now we're ready to work in the HelloMapView:</p>
+
+<ol>
+ <li>First we need some more types. Add the following at the top of the HelloMapView class:
+
+<pre>
+List<Overlay> mapOverlays;
+Drawable drawable;
+HelloItemizedOverlay itemizedOverlay;</pre></li>
+
+ <li>Now pick up where we left off in the <code>onCreate()</code> method. Instantiate the
+ new fields:
+
+<pre>
+mapOverlays = mapView.getOverlays();
+drawable = this.getResources().getDrawable(R.drawable.androidmarker);
+itemizedoverlay = new HelloItemizedOverlay(drawable);</pre>
+
+ <p>All overlay elements on a map are held by the MapView, so when we want to add some, we must
+ first retrieve the List with <code>getOverlays()</code> methods. We instantiate the Drawable, which will
+ be used as our map marker, by using our Context resources to get the Drawable we placed in
+ the res/drawable/ directory (androidmarker.png). Our HelloItemizedOverlay takes the Drawable in order to set the
+ default marker.</p></li>
+
+ <li>Now let's make our first OverlayItem by creating a GeoPoint
+ that defines our map coordinates, then pass it to a new OverlayItem:
+
+<pre>
+GeoPoint point = new GeoPoint(19240000,-99120000);
+OverlayItem overlayitem = new OverlayItem(point, "", "");</pre>
+
+ <p>GeoPoint coordinates are based in microdegrees (degrees * 1e6). The OverlayItem takes this
+ GeoPoint and two strings. Here, we won't concern ourselves with the strings, which can display
+ text when we click our marker, because we haven't yet written the click handler for the OverlayItem.</p></li>
+
+ <li>All that's left is for us to add this OverlayItem to our collection in the HelloItemizedOverlay,
+ and add this to the List of Overlay objects retrieved from the MapView:
+
+<pre>
+itemizedoverlay.addOverlay(overlayitem);
+mapOverlays.add(itemizedoverlay);</pre></li>
+
+ <li>Run it!</li>
+</ol>
+
+<p>We've sent our droid to Mexico City. Hola, Mundo!</p>
+<p>You should see the following:</p>
+<img src="images/hello-mapview.png" width="150px" />
+
+<p>Because we created our ItemizedOverlay class with an ArrayList, we can continue adding new
+OverlayItems. Try adding another one. Before the <code>addOverlay()</code> method is called, add these lines:</p>
+<pre>
+GeoPoint point2 = new GeoPoint(35410000, 139460000);
+OverlayItem overlayitem2 = new OverlayItem(point2, "", "");
+</pre>
+<p>Run it again... We've sent a new droid to Tokyo. Sekai, konichiwa!</p>
+
diff --git a/docs/html/resources/tutorials/views/hello-relativelayout.jd b/docs/html/resources/tutorials/views/hello-relativelayout.jd
new file mode 100644
index 0000000..1b91537
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-relativelayout.jd
@@ -0,0 +1,75 @@
+page.title=Hello, RelativeLayout
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>A {@link android.widget.RelativeLayout} is a ViewGroup that allows you to layout child elements
+in positions relative to the parent or siblings elements.</p>
+
+<ol>
+ <li>Start a new project/Activity called HelloRelativeLayout.</li>
+ <li>Open the layout file. Make it like so:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="Type here:"/>
+
+ <EditText
+ android:id="@+id/entry"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:drawable/editbox_background"
+ android:layout_below="@id/label"/>
+
+ <Button
+ android:id="@+id/ok"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/entry"
+ android:layout_alignParentRight="true"
+ android:layout_marginLeft="10dip"
+ android:text="OK" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@id/ok"
+ android:layout_alignTop="@id/ok"
+ android:text="Cancel" />
+
+</RelativeLayout>
+</pre>
+<p>Pay attention to each of the additional <code>layout_*</code> attributes (besides the
+usual width and height, which are required for all elements). When using relative layout,
+we use attributes like <code>layout_below</code> and <code>layout_toLeftOf</code> to describe
+how we'd like to position each View. Naturally, these are different relative positions, and the
+value of the attribute is the id of the element we want the position relative to.</p>
+</li>
+<li>Make sure your Activity loads this layout in the <code>onCreate()</code> method:</p>
+<pre>
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+}
+</pre>
+<p><code>R.layout.main</code> refers to the <code>main.xml</code> layout file.</p>
+</li>
+<li>Run it.</li>
+</ol>
+<p>You should see the following:</p>
+<img src="images/hello-relativelayout.png" width="150px" />
+
+<h3>Resources</h3>
+<ul>
+ <li>{@link android.widget.RelativeLayout}</li>
+ <li>{@link android.widget.TextView}</li>
+ <li>{@link android.widget.EditText}</li>
+ <li>{@link android.widget.Button}</li>
+</ul>
diff --git a/docs/html/resources/tutorials/views/hello-spinner.jd b/docs/html/resources/tutorials/views/hello-spinner.jd
new file mode 100644
index 0000000..3a04214
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-spinner.jd
@@ -0,0 +1,106 @@
+page.title=Hello, Spinner
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>A {@link android.widget.Spinner} is a widget that allows the user to select an item from a group.
+It is similar to a dropdown list and will allow scrolling when the
+list exceeds the available vertical space on the screen.</p>
+
+
+<ol>
+ <li>Start a new project/Activity called HelloSpinner.</li>
+ <li>Open the layout file.
+ Make it like so:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:padding="10dip"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:text="Please select a planet:"
+ />
+
+ <Spinner
+ android:id="@+id/spinner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:drawSelectorOnTop="true"
+ android:prompt="@string/planet_prompt"
+ />
+
+</LinearLayout>
+</pre>
+ <p>Notice that the Spinner's <code>android:prompt</code> is a string resource. In
+ this case, Android does not allow it to be a string, it must be a reference to a resource.
+ So...</p>
+</li>
+
+<li>Open the strings.xml file in res/values/ and add the following <code><string></code>
+element inside the <code><resources></code> element:
+<pre>
+<string name="planet_prompt">Choose a planet</string>
+</pre>
+</li>
+
+<li>Create a new XML file in res/values/ called arrays.xml. Insert the following:
+<pre>
+<resources>
+
+ <string-array name="planets">
+ <item>Mercury</item>
+ <item>Venus</item>
+ <item>Earth</item>
+ <item>Mars</item>
+ <item>Jupiter</item>
+ <item>Saturn</item>
+ <item>Uranus</item>
+ <item>Neptune</item>
+ </string-array>
+
+</resources>
+</pre>
+ <p>This is the list of items (planets) that the user can select from in the Spinner widget.</p>
+</li>
+
+<li>Now open the HelloSpinner.java file. Insert the following code into the HelloSpinner class:
+<pre>
+@Override
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ Spinner s = (Spinner) findViewById(R.id.spinner);
+ ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
+ this, R.array.planets, android.R.layout.simple_spinner_item);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ s.setAdapter(adapter);
+}
+</pre>
+ <p>That's it. We start by creating a Spinner from our layout. We then create an {@link android.widget.ArrayAdapter}
+ that binds each element of our string array to a layout view—we pass <code>createFromResource</code> our Context,
+ the array of selectable items and the type of layout we'd like each one bound to. We then call
+ <code>setDropDownViewResource()</code> to define the type of layout in which to present the
+ entire collection. Finally, we set this Adapter to associate with our Spinner,
+ so the string items have a place to go.</p>
+</li>
+
+<li>Now run it.</li>
+</ol>
+<p>It should look like this:</p>
+<img src="images/hello-spinner.png" width="150px" />
+
+
+<h3>Resources</h3>
+<ul>
+ <li>{@link android.R.layout}</li>
+ <li>{@link android.widget.ArrayAdapter}</li>
+ <li>{@link android.widget.Spinner}</li>
+</ul>
+
diff --git a/docs/html/resources/tutorials/views/hello-tablelayout.jd b/docs/html/resources/tutorials/views/hello-tablelayout.jd
new file mode 100644
index 0000000..83d6f5d
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-tablelayout.jd
@@ -0,0 +1,118 @@
+page.title=Hello, TableLayout
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>A {@link android.widget.TableLayout} is a ViewGroup that
+will lay child View elements into rows and columns.</p>
+
+
+<ol>
+ <li>Start a new project/Activity called HelloTableLayout.</li>
+ <li>Open the layout file.
+ Make it like so:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:stretchColumns="1">
+
+ <TableRow>
+ <TextView
+ android:layout_column="1"
+ android:text="Open..."
+ android:padding="3dip" />
+ <TextView
+ android:text="Ctrl-O"
+ android:gravity="right"
+ android:padding="3dip" />
+ </TableRow>
+
+ <TableRow>
+ <TextView
+ android:layout_column="1"
+ android:text="Save..."
+ android:padding="3dip" />
+ <TextView
+ android:text="Ctrl-S"
+ android:gravity="right"
+ android:padding="3dip" />
+ </TableRow>
+
+ <TableRow>
+ <TextView
+ android:layout_column="1"
+ android:text="Save As..."
+ android:padding="3dip" />
+ <TextView
+ android:text="Ctrl-Shift-S"
+ android:gravity="right"
+ android:padding="3dip" />
+ </TableRow>
+
+ <View
+ android:layout_height="2dip"
+ android:background="#FF909090" />
+
+ <TableRow>
+ <TextView
+ android:text="X"
+ android:padding="3dip" />
+ <TextView
+ android:text="Import..."
+ android:padding="3dip" />
+ </TableRow>
+
+ <TableRow>
+ <TextView
+ android:text="X"
+ android:padding="3dip" />
+ <TextView
+ android:text="Export..."
+ android:padding="3dip" />
+ <TextView
+ android:text="Ctrl-E"
+ android:gravity="right"
+ android:padding="3dip" />
+ </TableRow>
+
+ <View
+ android:layout_height="2dip"
+ android:background="#FF909090" />
+
+ <TableRow>
+ <TextView
+ android:layout_column="1"
+ android:text="Quit"
+ android:padding="3dip" />
+ </TableRow>
+</TableLayout>
+</pre>
+<p>Notice how this resembles the structure of an HTML table. <code>TableLayout</code> is like the
+<code>table</code> element; <code>TableRow</code> is like a <code>tr</code> element; but for our cells like
+the html <code>td</code> element, we can use any kind of View. Here, we use <code>TextView</code> for the cells.</p>
+
+</li>
+<li>Make sure your Activity loads this layout in the <code>onCreate()</code> method:
+<pre>
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+}
+</pre>
+<p><code>R.layout.main</code> refers to the <code>main.xml</code> layout file.</p>
+</li>
+<li>Run it.</li>
+</ol>
+<p>You should see the following:</p>
+<img src="images/hello-tablelayout.png" width="150px" />
+
+<h3>References</h3>
+<ul>
+ <li>{@link android.widget.TableLayout}</li>
+ <li>{@link android.widget.TableRow}</li>
+ <li>{@link android.widget.TextView}</li>
+</ul>
+
+
diff --git a/docs/html/resources/tutorials/views/hello-tabwidget.jd b/docs/html/resources/tutorials/views/hello-tabwidget.jd
new file mode 100644
index 0000000..8424616
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-tabwidget.jd
@@ -0,0 +1,124 @@
+page.title=Hello, TabWidget
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>A {@link android.widget.TabWidget} offers the ability to easily draw an interface that uses
+tabs to navigate between different views.</p>
+
+<ol>
+ <li>Start a new project/Activity called HelloTabWidget.</li>
+ <li>Open the layout file and make it like so:</li>
+ <pre>
+<?xml version="1.0" encoding="utf-8"?>
+<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/tabhost"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView
+ android:id="@+id/textview1"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:text="this is a tab" />
+ <TextView
+ android:id="@+id/textview2"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:text="this is another tab" />
+ <TextView
+ android:id="@+id/textview3"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:text="this is a third tab" />
+ </FrameLayout>
+ </LinearLayout>
+</TabHost>
+</pre>
+ <p>Here, we've created a {@link android.widget.TabHost} that contains the entire layout of the Activity.
+ A TabHost requires two descendant elements: a {@link android.widget.TabWidget} and a {@link android.widget.FrameLayout}.
+ In order to properly layout these elements, we've put them inside a vertical {@link android.widget.LinearLayout}.
+ The FrameLayout is where we keep the content that will change with each tab. Each child in the FrameLayout will
+ be associated with a different tab.
+ In this case, each tab simply shows a different {@link android.widget.TextView} with some text. </p>
+ <p>Notice that the TabWidget and the FrameLayout elements have specific <code>android</code> namespace IDs. These are necessary
+ so that the TabHost can automatically retireve references to them, populate the TabWidget with the tabs that we'll define
+ in our code, and swap the views in the FrameLayout. We've also defined our own IDs for each TextView, which we'll use to
+ associate each tab with the view that it should reveal.</p>
+ <p>Of course, you can
+ make these child views as large as complex as you'd like — instead of the TextView elements,
+ you could start with other layout views and build a unique layout hierarchy for each tab.</p>
+ </li>
+ <li>Now we'll add our code. Open HelloTabWidget.java and make it a <code>TabActivity</code>.
+ <p>By default, Eclipse creates a class that extends <code>Activity</code>. Change it to
+ extend <code>TabActivity</code>:</p>
+ <pre>
+public class HelloTabWidget extends TabActivity {
+</pre>
+ </li>
+ <li>Now fill in the the <code>onCreate</code> method like this:
+ <pre>
+public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ mTabHost = getTabHost();
+
+ mTabHost.addTab(mTabHost.newTabSpec("tab_test1").setIndicator("TAB 1").setContent(R.id.textview1));
+ mTabHost.addTab(mTabHost.newTabSpec("tab_test2").setIndicator("TAB 2").setContent(R.id.textview2));
+ mTabHost.addTab(mTabHost.newTabSpec("tab_test3").setIndicator("TAB 3").setContent(R.id.textview3));
+
+ mTabHost.setCurrentTab(0);
+}
+</pre>
+ <p>As usual, we start by setting our layout.</p>
+ <p>We then call the TabActivity method <code>getTabHost()</code>,
+ which returns us a reference to the TabHost we created in our layout. Upon our TabHost, we call <code>addTab()</code>
+ for each of the tabs that we want to add to the TabWidget. Each time we call this, we pass a
+ {@link android.widget.TabHost.TabSpec} that we build on the fly, and with it, chain together two necessary methods:
+ <code>setIndicator()</code> to set the text for the tab button, and <code>setContent()</code> to define
+ which View we want to associate with the tab and reveal when pressed. Our indicator is just a text string and
+ our content is an ID reference to the TextView elements we inserted in the FrameLayout.</p>
+ <p>At the end, we call <code>setCurrentTab()</code> to define which tab should be opened by default. The tabs
+ are saved like a zero-based array, so to open the first tab, we pass zero (<var>0</var>).</p>
+ </li>
+ <li>To clean-up the presentation a bit more, let's remove the window title that appears at the top of the layout.
+ Android includes a theme that removes that title for us. To add it, open the Android Manifest file and add
+ the <var>NoTitleBar</var> theme to the <code><application></code> tag. It should end up like this:
+ <pre>
+<application android:icon="@drawable/icon" android:theme="@android:style/Theme.NoTitleBar">
+</pre>
+ </li>
+ <li>That's it. Run your application.</li>
+
+</ol>
+
+
+<p>Your application should look like this:</p>
+<img src="images/hello-tabwidget.png" width="150px" />
+
+<div class="special"><p>You can include icons in your tabs by passing a
+{@link android.graphics.drawable.Drawable} when you call <code>setIndicator()</code>. Here's an example
+that uses a Drawable created from an image in the project resources:</p>
+<pre>setIndicator("TAB 1", getResources().getDrawable(R.drawable.tab_icon))</pre>
+</div>
+
+<h3>References</h3>
+<ul>
+<li>{@link android.widget.TabWidget}</li>
+<li>{@link android.widget.TabHost}</li>
+<li>{@link android.widget.TabHost.TabSpec}</li>
+<li>{@link android.widget.FrameLayout}</li>
+</ul>
+
diff --git a/docs/html/resources/tutorials/views/hello-timepicker.jd b/docs/html/resources/tutorials/views/hello-timepicker.jd
new file mode 100644
index 0000000..1a6c8f9
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-timepicker.jd
@@ -0,0 +1,159 @@
+page.title=Hello, TimePicker
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>A {@link android.widget.TimePicker} is a widget that allows the
+user to select the time by hour, minute and AM or PM.</p>
+
+
+<ol>
+ <li>Start a new project/Activity called HelloTimePicker.</li>
+ <li>Open the layout file and make it like so:
+ <pre>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView android:id="@+id/timeDisplay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""/>
+
+ <Button android:id="@+id/pickTime"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Change the time"/>
+
+</LinearLayout>
+</pre>
+ <p>For the layout, we're using a vertical LinearLayout, with a {@link android.widget.TextView} that
+ will display the time and a {@link android.widget.Button} that will initiate the
+ {@link android.widget.TimePicker} dialog.
+ With this layout, the TextView will sit above the Button.
+ The text value in the TextView is set empty, as it will be filled by our Activity
+ with the current time.</p>
+ </li>
+
+ <li>Open HelloTimePicker.java. Insert the following to the HelloTimePicker class:
+<pre>
+private TextView mTimeDisplay;
+private Button mPickTime;
+
+private int mHour;
+private int mMinute;
+
+static final int TIME_DIALOG_ID = 0;
+
+@Override
+protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ // capture our View elements
+ mTimeDisplay = (TextView) findViewById(R.id.timeDisplay);
+ mPickTime = (Button) findViewById(R.id.pickTime);
+
+ // add a click listener to the button
+ mPickTime.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ showDialog(TIME_DIALOG_ID);
+ }
+ });
+
+ // get the current time
+ final Calendar c = Calendar.getInstance();
+ mHour = c.get(Calendar.HOUR_OF_DAY);
+ mMinute = c.get(Calendar.MINUTE);
+
+ // display the current date
+ updateDisplay();
+}
+</pre>
+<p class="note"><strong>Tip:</strong> Press Ctrl(or Cmd) + Shift + O to import all needed packages.</p>
+ <p>We start by instantiating variables for our View elements and time fields.
+ The <code>TIME_DIALOG_ID</code> is a static integer that uniquely identifies the dialog. In the
+ <code>onCreate()</code> method, we get prepared by setting the layout and capturing the View elements.
+ We then set an on-click listener for the Button, so that when it is clicked, it will
+ show our TimePicker dialog. The <code>showDialog()</code> method will perform a callback
+ to our Activity. (We'll define this callback in the next section.) We then create an
+ instance of {@link java.util.Calendar} and get the current hour and minute. Finally, we call
+ <code>updateDisplay()</code>—our own method that will fill the TextView with the time.</p>
+</li>
+
+<li>After the <code>onCreate()</code> method, add the <code>onCreateDialog()</code> callback method:
+<pre>
+@Override
+protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case TIME_DIALOG_ID:
+ return new TimePickerDialog(this,
+ mTimeSetListener, mHour, mMinute, false);
+ }
+ return null;
+}
+</pre>
+ <p>This is passed the identifier we gave <code>showDialog()</code> and initializes
+ the TimePicker to the time we retrieved from our Calendar instance. It will be called by
+ <code>showDialog()</code>.</p>
+</li>
+
+<li>Now add our <code>updateDisplay()</code> method:
+<pre>
+// updates the time we display in the TextView
+private void updateDisplay() {
+ mTimeDisplay.setText(
+ new StringBuilder()
+ .append(pad(mHour)).append(":")
+ .append(pad(mMinute)));
+}
+</pre>
+ <p>This simply takes our member fields for the time and inserts them in
+ the <code>mTimeDisplay</code> TextView. Note that we call a new method, <code>pad()</code>,
+ on the hour and minute. (We'll create this method in the last step.)</p>
+</li>
+
+<li>Next, add a listener to be called when the time is reset:
+<pre>
+// the callback received when the user "sets" the time in the dialog
+private TimePickerDialog.OnTimeSetListener mTimeSetListener =
+ new TimePickerDialog.OnTimeSetListener() {
+ public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
+ mHour = hourOfDay;
+ mMinute = minute;
+ updateDisplay();
+ }
+ };
+</pre>
+ <p>Now when the user is done setting the time (clicks the "Set" button), we update our member fields with
+ the new time and update our TextView.</p>
+</li>
+<li>Finally, add the <code>pad()</code> method that we called from the <code>updateDisplay()</code>:
+<pre>
+private static String pad(int c) {
+ if (c >= 10)
+ return String.valueOf(c);
+ else
+ return "0" + String.valueOf(c);
+}
+</pre>
+ <p>This method returns the appropriate String representation of the hour or minute.
+ It will prefix a zero to the number if it's a single digit.
+ </p>
+</li>
+
+<li>Now run it.</li>
+</ol>
+<p>When you press the "Change the time" button, you should see the following:</p>
+<img src="images/hello-timepicker.png" width="150px" />
+
+<h3>References</h3>
+<ol>
+ <li>{@link android.widget.TimePicker}</li>
+ <li>{@link android.widget.Button}</li>
+ <li>{@link android.widget.TextView}</li>
+ <li>{@link java.util.Calendar}</li>
+</ol>
+
diff --git a/docs/html/resources/tutorials/views/hello-webview.jd b/docs/html/resources/tutorials/views/hello-webview.jd
new file mode 100644
index 0000000..c4388ea
--- /dev/null
+++ b/docs/html/resources/tutorials/views/hello-webview.jd
@@ -0,0 +1,118 @@
+page.title=Hello, WebView
+parent.title=Hello, Views
+parent.link=index.html
+@jd:body
+
+<p>A {@link android.webkit.WebView} allows you to create your own web browser Activity. In this tutorial,
+we'll create a simple Activity that can view web pages.</p>
+
+<ol>
+ <li>Create a new project/Activity called HelloWebView.</li>
+ <li>Open the layout file. Insert a WebView so it looks like so:
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <WebView
+ android:id="@+id/webview"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ />
+
+</LinearLayout>
+</pre></li>
+
+ <li>Now open the HelloWebView.java file.
+ At the top of the class, instantiate a WebView object:
+<pre>WebView webview;</pre>
+ <p> Then add the following at the end of the <code>onCreate()</code> method:</p>
+<pre>
+webview = (WebView) findViewById(R.id.webview);
+webview.getSettings().setJavaScriptEnabled(true);
+webview.loadUrl("http://www.google.com");
+</pre>
+
+ <p>This captures the WebView we created in our layout, then requests a
+ {@link android.webkit.WebSettings} object and enables JavaScript.
+ Then we load a URL.</p></li>
+
+ <li>Because we're accessing the internet, we need to add the appropriate
+ permissions to the Android manifest file. So open the AndroidManifest.xml file
+ and, add the following as a child of the <code><manifest></code> element:
+
+ <pre><uses-permission android:name="android.permission.INTERNET" /></pre></li>
+
+ <li>Now run it.</li>
+</ol>
+<p> You now have the world's simplest web page viewer.
+ It's not quite a browser yet. It only loads the page we've requested.</p>
+
+<hr/>
+
+<p>We can load a page, but as soon as we click a link, the default Android web browser
+handles the Intent, instead of our own WebView handling the action. So now we'll
+override the {@link android.webkit.WebViewClient} to enable us to handle our own URL loading.</p>
+
+<ol>
+ <li>In the HelloAndroid Activity, add this nested private class:
+<pre>
+private class HelloWebViewClient extends WebViewClient {
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ view.loadUrl(url);
+ return true;
+ }
+}</pre></li>
+
+ <li>Now, in the <code>onCreate()</code> method, set an instance of the <code>HelloWebViewClient</code>
+ as our WebViewClient:
+ <pre>webview.setWebViewClient(new WebViewClientDemo());</pre>
+
+ <p>This line should immediately follow the initialization of our WebView object.</p>
+ <p>What we've done is create a WebViewClient that will load any URL selected in our
+WebView in the same WebView. You can see this in the <code>shouldOverrideUrlLoading()</code>
+method, above—it is passed the current WebView and the URL, so all we do
+is load the URL in the given view. Returning <var>true</var> says that we've handled the URL
+ourselves and the event should not bubble-up.</p>
+ <p>If you try it again, new pages will now load in the HelloWebView Activity. However, you'll notice that
+we can't navigate back. We need to handle the back button
+on the device, so that it will return to the previous page, rather than exit the application.</p>
+ </li>
+
+ <li>To handle the back button key press, add the following method inside the HelloWebView
+Activity:
+<pre>
+@Override
+public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {
+ webview.goBack();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+}</pre>
+ <p>The condition uses a {@link android.view.KeyEvent} to check
+ whether the key pressed is the BACK button and whether the
+ WebView is actually capable of navigating back (if it has a history). If both are
+ <em>not</em> true, then we send the event up the chain (and the Activity will close).
+ But if both <em>are</em> true, then we call <code>goBack()</code>,
+ which will navigate back one step in the history. We then return true to indicate
+ that we've handled the event.</p>
+</li>
+</ol>
+<p>When you open the application, it should look like this:</p>
+<img src="images/hello-webview.png" width="150px" />
+
+<h3>Resource</h3>
+<ul>
+<li>{@link android.webkit.WebView}</li>
+<li>{@link android.webkit.WebViewClient}</li>
+<li>{@link android.view.KeyEvent}</li>
+</ul>
+
+
+
+
+
diff --git a/docs/html/resources/tutorials/views/images/android.png b/docs/html/resources/tutorials/views/images/android.png
new file mode 100755
index 0000000..39a1ac7
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/android.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/androidmarker.png b/docs/html/resources/tutorials/views/images/androidmarker.png
new file mode 100755
index 0000000..8c43d46
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/androidmarker.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-autocomplete.png b/docs/html/resources/tutorials/views/images/hello-autocomplete.png
new file mode 100755
index 0000000..e1fd80d
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-autocomplete.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-datepicker.png b/docs/html/resources/tutorials/views/images/hello-datepicker.png
new file mode 100755
index 0000000..2075066
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-datepicker.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-formstuff.png b/docs/html/resources/tutorials/views/images/hello-formstuff.png
new file mode 100755
index 0000000..3b4bf54
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-formstuff.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-gallery.png b/docs/html/resources/tutorials/views/images/hello-gallery.png
new file mode 100755
index 0000000..22d1eaf
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-gallery.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-gridview.png b/docs/html/resources/tutorials/views/images/hello-gridview.png
new file mode 100755
index 0000000..2def0df
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-gridview.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-linearlayout.png b/docs/html/resources/tutorials/views/images/hello-linearlayout.png
new file mode 100755
index 0000000..dfef819
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-linearlayout.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-listview.png b/docs/html/resources/tutorials/views/images/hello-listview.png
new file mode 100755
index 0000000..a1cf7aa
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-listview.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-mapview.png b/docs/html/resources/tutorials/views/images/hello-mapview.png
new file mode 100755
index 0000000..0956760
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-mapview.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-relativelayout.png b/docs/html/resources/tutorials/views/images/hello-relativelayout.png
new file mode 100755
index 0000000..ec4d9d4
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-relativelayout.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-spinner.png b/docs/html/resources/tutorials/views/images/hello-spinner.png
new file mode 100755
index 0000000..42e2a91
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-spinner.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-tablelayout.png b/docs/html/resources/tutorials/views/images/hello-tablelayout.png
new file mode 100755
index 0000000..3d80e7f
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-tablelayout.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-tabwidget.png b/docs/html/resources/tutorials/views/images/hello-tabwidget.png
new file mode 100644
index 0000000..6a52356
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-tabwidget.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-timepicker.png b/docs/html/resources/tutorials/views/images/hello-timepicker.png
new file mode 100755
index 0000000..bd5a1ee
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-timepicker.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-webview.png b/docs/html/resources/tutorials/views/images/hello-webview.png
new file mode 100755
index 0000000..283ce7d
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/hello-webview.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/index.jd b/docs/html/resources/tutorials/views/index.jd
new file mode 100644
index 0000000..2cb5d3a
--- /dev/null
+++ b/docs/html/resources/tutorials/views/index.jd
@@ -0,0 +1,118 @@
+page.title=Hello, Views
+@jd:body
+
+<style>
+.view {float:left; margin:10px; font-size:120%; font-weight:bold;}
+.view img {border:1px solid black; margin:5px 0 0; padding:5px;}
+</style>
+
+<p>This collection of "Hello World"-style tutorials is designed
+to get you quickly started with common Android Views and widgets. The aim is to let you copy and paste
+these kinds of boring bits so you can focus on developing the code that makes your Android application rock.
+Of course, we'll discuss some of the given code so that it all makes sense.</p>
+
+<p>Note that a certain amount of knowledge is assumed for these tutorials. If you haven't
+completed the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello, World</a> tutorial,
+please do so—it will teach you many things you should know about basic
+Android development and Eclipse features. More specifically, you should know:</p>
+<ul>
+ <li>How to create a new Android project.</li>
+ <li>The basic structure of an Android project (resource files, layout files, etc.).</li>
+ <li>The essential components of an {@link android.app.Activity}.</li>
+ <li>How to build and run a project.</li>
+</ul>
+<p>Please, also notice that, in order to make these tutorials simple, some may
+not convey the better Android coding practices. In particular, many of them
+use hard-coded strings in the layout files—the better practice is to reference strings from
+your strings.xml file.</p>
+<p>With this knowledge, you're ready to begin, so take your pick.</p>
+
+<div>
+
+<div class="view">
+<a href="hello-linearlayout.html">LinearLayout</a><br/>
+<a href="hello-linearlayout.html"><img src="images/hello-linearlayout.png" height="285" width="200" /></a>
+</div>
+<div class="view">
+<a href="hello-relativelayout.html">RelativeLayout</a><br/>
+<a href="hello-relativelayout.html"><img src="images/hello-relativelayout.png" height="285" width="200" /></a>
+</div>
+<div class="view">
+<a href="hello-tablelayout.html">TableLayout</a><br/>
+<a href="hello-tablelayout.html"><img src="images/hello-tablelayout.png" height="285" width="200" /></a>
+</div>
+
+<div class="view">
+<a href="hello-datepicker.html">DatePicker</a><br/>
+<a href="hello-datepicker.html"><img src="images/hello-datepicker.png" height="285" width="200" /></a>
+</div>
+
+<div class="view">
+<a href="hello-timepicker.html">TimePicker</a><br/>
+<a href="hello-timepicker.html"><img src="images/hello-timepicker.png" height="285" width="200" /></a>
+</div>
+<div class="view">
+<a href="hello-formstuff.html">Form Stuff</a><br/>
+<a href="hello-formstuff.html"><img src="images/hello-formstuff.png" height="285" width="200" /></a>
+</div>
+<div class="view">
+<a href="hello-spinner.html">Spinner</a><br/>
+<a href="hello-spinner.html"><img src="images/hello-spinner.png" height="285" width="200" /></a>
+</div>
+
+<div class="view">
+<a href="hello-autocomplete.html">AutoComplete</a><br/>
+<a href="hello-autocomplete.html"><img src="images/hello-autocomplete.png" height="285" width="200" /></a>
+</div>
+<div class="view">
+<a href="hello-listview.html">ListView</a><br/>
+<a href="hello-listview.html"><img src="images/hello-listview.png" height="285" width="200" /></a>
+</div>
+<div class="view">
+<a href="hello-gridview.html">GridView</a><br/>
+<a href="hello-gridview.html"><img src="images/hello-gridview.png" height="285" width="200" /></a>
+</div>
+
+<div class="view">
+<a href="hello-gallery.html">Gallery</a><br/>
+<a href="hello-gallery.html"><img src="images/hello-gallery.png" height="285" width="200" /></a>
+</div>
+
+<div class="view">
+<a href="hello-tabwidget.html">TabWidget</a><br/>
+<a href="hello-tabwidget.html"><img src="images/hello-tabwidget.png" height="285" width="200" /></a>
+</div>
+
+<div class="view">
+<a href="hello-mapview.html">MapView</a><br/>
+<a href="hello-mapview.html"><img src="images/hello-mapview.png" height="285" width="200" /></a>
+</div>
+
+<div class="view">
+<a href="hello-webview.html">WebView</a><br/>
+<a href="hello-webview.html"><img src="images/hello-webview.png" height="285" width="200" /></a>
+</div>
+
+<!--
+TODO
+
+<div class="view">
+<a href="hello-popupwindow.html">PopupWindow<br/>
+<img src="images/hello-popupwindow.png" height="285" width="200" /></a>
+</div>
+<div class="view">
+<a href="hello-tabhost.html">TabHost / TabWidget<br/>
+<img src="images/hello-tabhost.png" height="285" width="200" /></a>
+</div>
+ProgressBar; RatingBar; FrameLayout
+
+-->
+
+<p class="note" style="clear:left">
+There are plenty more Views and widgets available. See the {@link android.view.View} class
+for more on View layouts, and the {@link android.widget widget package}
+for more useful widgets. And for more raw code samples, visit the
+<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/index.html">Api Demos</a>.
+These can also be found offline, in <code>/<sdk>/samples/ApiDemos</code>.</p>
+</div>
+
diff --git a/docs/html/sdk/1.1_r1/upgrading.jd b/docs/html/sdk/1.1_r1/upgrading.jd
index 19095c0..5628d04 100644
--- a/docs/html/sdk/1.1_r1/upgrading.jd
+++ b/docs/html/sdk/1.1_r1/upgrading.jd
@@ -145,6 +145,6 @@
to the new SDK, note that you will need to uninstall the version of ApiDemos that comes
preinstalled in the emulator. For more information, or if you encounter an "reinstallation"
error when running or installing ApiDemos, see the troubleshooting topic
-<a href="{@docRoot}guide/appendix/faq/troubleshooting.html#apidemosreinstall">I can't install ApiDemos
+<a href="{@docRoot}resources/faq/troubleshooting.html#apidemosreinstall">I can't install ApiDemos
apps in my IDE because of a signing error</a> for information about how to solve the problem.</p>
diff --git a/docs/html/sdk/RELEASENOTES.jd b/docs/html/sdk/RELEASENOTES.jd
index 5c3abf8..ae86a4b 100644
--- a/docs/html/sdk/RELEASENOTES.jd
+++ b/docs/html/sdk/RELEASENOTES.jd
@@ -604,7 +604,7 @@
classes, you can do so. However, you need to set up a custom JUnit configuration
before your tests will run properly. For detailed information about how to set
up the JUnit configuration, see the troubleshooting topic <a
-href="{@docRoot}guide/appendix/faq/troubleshooting.html#addjunit">Running a Junit test class
+href="{@docRoot}resources/faq/troubleshooting.html#addjunit">Running a Junit test class
in Eclipse</a>.</li>
</ul>
diff --git a/docs/html/sdk/android-2.0.1.jd b/docs/html/sdk/android-2.0.1.jd
index fbce45e..43b1710 100644
--- a/docs/html/sdk/android-2.0.1.jd
+++ b/docs/html/sdk/android-2.0.1.jd
@@ -297,9 +297,8 @@
<p>For a detailed view of API changes in Android {@sdkPlatformVersion} (API Level {@sdkPlatformApiLevel}), as compared to
API Level 5, see the <a
href="{@docRoot}sdk/api_diff/{@sdkPlatformApiLevel}/changes.html">API
-Differences Report</a>. Note that this difference report compares only to
-the most recent API Level, and there are few changes, so to see changes
-introduces in Android 2.0 (API Level 5), see the <a
+Differences Report</a>. There are very few API changes in API Level 6,
+so you might also be interested in reviewing the <a
href="{@docRoot}sdk/api_diff/5/changes.html">API
-Differences between 4 and 5</a>.</p>
+differences between 4 and 5</a>.</p>
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index 7d9efdb..f861afd 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -53,6 +53,11 @@
installed on your computer (3.4 or newer is recommended). If you need
to install Eclipse, you can download it from this location: </p>
+<p style="margin-left:2em;"><a href=
+"http://www.eclipse.org/downloads/">http://www.eclipse.org/downloads/</a>
+</p>
+
+<p>A Java or RCP version of Eclipse is recommended. </p>
<h2 id="installing">Installing the ADT Plugin</h2>
@@ -196,7 +201,7 @@
installing ADT, your Eclipse installion might not include these components.
For information about how to quickly add the necessary components to your
Eclipse installation, see the troubleshooting topic
-<a href="{@docRoot}guide/appendix/faq/troubleshooting.html#installeclipsecomponents">ADT
+<a href="{@docRoot}resources/faq/troubleshooting.html#installeclipsecomponents">ADT
Installation Error: "requires plug-in org.eclipse.wst.sse.ui"</a>.</p>
<h4>For Linux users</h4>
@@ -263,9 +268,10 @@
<p>If you encounter problems during the update of ADT, you
can try removing the existing ADT plugin and then performing a fresh
-installation. Fully remove your existing ADT Plugin as described in <a
-href="#uninstalling">Uninstalling the ADT Plugin</a>, below, and then follow the guide to <a
-href="#installing">Installing the ADT Plugin</a>.</p>
+installation. To remove the plugin, follow the instructions in <a
+href="#uninstalling">Uninstalling the ADT Plugin</a>, below. To reinstall
+the plugin, follow the instructions in <a
+href="#installing">Installing the ADT Plugin</a>, above.</p>
<h2 id="uninstalling">Uninstalling the ADT plugin</h2>
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 6ca02b6..209a67b 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -84,6 +84,6 @@
project or move existing applications into the new SDK.</p>
<p>If you are new to Android, you can use the <a
-href="{@docRoot}guide/tutorials/hello-world.html">Hello World</a> tutorial to
+href="{@docRoot}resources/tutorials/hello-world.html">Hello World</a> tutorial to
get started quickly. <a href="{@docRoot}sdk/installing.html#NextSteps">Next
Steps</a> offers other suggestions of how to begin. Welcome!</p>
diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd
index 261b49f..698c347 100644
--- a/docs/html/sdk/installing.jd
+++ b/docs/html/sdk/installing.jd
@@ -186,7 +186,7 @@
</ul>
<p>To develop <em>any</em> Android application, even if you are following the <a
-href="{@docRoot}guide/tutorials/hello-world.html">Hello World</a> tutorial, you
+href="{@docRoot}resources/tutorials/hello-world.html">Hello World</a> tutorial, you
<strong>must download at least one Android platform</strong> into your SDK.
Typically, you will want to download multiple platforms, so that you can build
your application on the lowest version you want to support, but test against
@@ -342,9 +342,9 @@
<p><strong>Explore some code</strong></p>
<ul>
- <li>Set up a <a href="{@docRoot}guide/tutorials/hello-world.html">Hello
+ <li>Set up a <a href="{@docRoot}resources/tutorials/hello-world.html">Hello
World application</a> (highly recommended, especially for Eclipse users)</li>
- <li>Follow the <a href="{@docRoot}guide/tutorials/notepad/index.html">
+ <li>Follow the <a href="{@docRoot}resources/tutorials/notepad/index.html">
Notepad Tutorial</a> to build a full Android application </li>
<li>Create a new project for one of the other sample applications
included in <code><em><sdk></em>/platforms/<em><platform></em>/samples</code>,
@@ -354,7 +354,7 @@
<p><strong>Visit the Android developer groups</strong></p>
<ul>
<li>Take a look at the <a
- href="{@docRoot}community/index.html">Community</a> tab to see a list of
+ href="{@docRoot}resources/community-groups.html">Community</a> pages to see a list of
Android developers groups. In particular, you might want to look at the
<a href="http://groups.google.com/group/android-developers">Android
Developers</a> group to get a sense for what the Android developer
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 4d2e048..70fba7e 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -500,7 +500,8 @@
}
int numChannels = sample->numChannels();
uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5);
- uint32_t bufferFrames = (afFrameCount * sampleRate) / afSampleRate;
+ uint32_t totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate;
+ uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount;
uint32_t frameCount = 0;
if (loop) {
@@ -509,13 +510,13 @@
#ifndef USE_SHARED_MEM_BUFFER
// Ensure minimum audio buffer size in case of short looped sample
- if(frameCount < kDefaultBufferCount * bufferFrames) {
- frameCount = kDefaultBufferCount * bufferFrames;
+ if(frameCount < totalFrames) {
+ frameCount = totalFrames;
}
#endif
AudioTrack* newTrack;
-
+
// mToggle toggles each time a track is started on a given channel.
// The toggle is concatenated with the SoundChannel address and passed to AudioTrack
// as callback user data. This enables the detection of callbacks received from the old
@@ -524,7 +525,7 @@
unsigned long toggle = mToggle ^ 1;
void *userData = (void *)((unsigned long)this | toggle);
uint32_t channels = (numChannels == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO;
-
+
#ifdef USE_SHARED_MEM_BUFFER
newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
channels, sample->getIMemory(), 0, callback, userData);
diff --git a/preloaded-classes b/preloaded-classes
index 1403b97..1b9a310 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -61,9 +61,6 @@
android.app.Service
android.app.ServiceConnectionLeaked
android.app.TabActivity
-android.content.AbstractSyncableContentProvider
-android.content.AbstractTableMerger
-android.content.AsyncQueryHandler$WorkerHandler
android.content.BroadcastReceiver
android.content.ComponentCallbacks
android.content.ComponentName
@@ -87,13 +84,8 @@
android.content.Intent$1
android.content.IntentFilter
android.content.SearchRecentSuggestionsProvider
-android.content.ServiceConnection
-android.content.SharedPreferences
android.content.SyncResult
-android.content.SyncResult$1
android.content.SyncStats
-android.content.SyncStats$1
-android.content.SyncableContentProvider
android.content.UriMatcher
android.content.pm.ActivityInfo
android.content.pm.ActivityInfo$1
@@ -697,7 +689,6 @@
com.google.android.mms.pdu.CharacterSets
com.google.android.mms.pdu.PduPart
com.google.android.mms.pdu.PduPersister
-com.google.android.net.GoogleHttpClient
com.google.android.net.UrlRules
com.google.android.net.UrlRules$Rule
com.ibm.icu4jni.charset.CharsetDecoderICU
diff --git a/tests/AndroidTests/src/com/android/unit_tests/DbSSLSessionCacheTest.java b/tests/AndroidTests/src/com/android/unit_tests/DbSSLSessionCacheTest.java
deleted file mode 100644
index 8d7d797..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/DbSSLSessionCacheTest.java
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests;
-
-import android.content.ContentResolver;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.provider.Settings;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.Suppress;
-
-import com.google.android.net.GoogleHttpClient;
-
-import com.android.internal.net.DbSSLSessionCache;
-import com.android.internal.net.DbSSLSessionCache.DatabaseHelper;
-
-import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.HttpGet;
-
-import java.io.IOException;
-import java.security.Principal;
-import java.security.cert.Certificate;
-
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSessionContext;
-import javax.security.cert.X509Certificate;
-
-/** Unit test for SSL session caching with {@link GoogleHttpClient}.
- * Uses network resources.
- */
-@Suppress
-public class DbSSLSessionCacheTest extends AndroidTestCase {
-
- protected void setUp() throws Exception {
- }
-
- protected void tearDown() throws Exception {
- }
-
- /**
- * We want to test the actual database write - the actual hooking into
- * low-level SSL is tested.
- */
- @LargeTest
- public void testSslCacheAdd() throws Exception {
- // Let's verify the database has the rows.
- // Use internal details of the implementation - could make the field
- // visible for testing, but it's same.
-
- // Use default database
- DbSSLSessionCache cache = DbSSLSessionCache.getInstanceForPackage(getContext());
- cache.clear();
-
-
- makeRequestInNewContext("https://www.google.com");
-
- // Verify the key was inserted
- SQLiteOpenHelper helper = new DatabaseHelper(getContext());
- Cursor query = null;
- try {
- query = helper.getReadableDatabase().query(DbSSLSessionCache.SSL_CACHE_TABLE,
- new String[] {"hostport"}, null,
- null, null, null, null);
-
- assertTrue(query.moveToFirst()); // one row inserted
- String hostPort = query.getString(0);
- assertEquals(hostPort, "www.google.com:443");
- } finally {
- query.close();
- }
- }
-
- @LargeTest
- public void testExpire() throws Exception {
- DatabaseHelper helper = new DatabaseHelper(getContext());
- // clean up
- DbSSLSessionCache cache = new DbSSLSessionCache(helper);
- cache.clear();
-
- long t0 = System.currentTimeMillis();
- for (int i = 0; i < DbSSLSessionCache.MAX_CACHE_SIZE + 2; i++) {
- final int port = i;
- cache.putSessionData(new MockSession() {
-
- public String getPeerHost() {
- return "test.host.com";
- }
-
- public int getPeerPort() {
- return port;
- }
- }, new byte[256]);
- }
- long t1 = System.currentTimeMillis();
-
- System.err.println("Time to insert " +
- (DbSSLSessionCache.MAX_CACHE_SIZE + 2) + " " + (t1 - t0));
-
- // first entry should have port 1.
- Cursor query = helper.getReadableDatabase().query(DbSSLSessionCache.SSL_CACHE_TABLE,
- new String[] {"hostport", "session"}, null,
- null, null, null, null);
-
- int cnt = query.getCount();
-
- assertTrue(query.moveToFirst()); // one row inserted
- String hostPort = query.getString(0);
- assertEquals("test.host.com:2", hostPort);
- while (query.moveToNext()) {
- hostPort = query.getString(0);
- String session = query.getString(1);
- }
- long t2 = System.currentTimeMillis();
- System.err.println("Time to load " + cnt + " " + (t2 - t1));
-
- query.close();
- }
-
- private void makeRequestInNewContext(String url) throws IOException {
- GoogleHttpClient client = new GoogleHttpClient(getContext(), "Test",
- false /* no gzip */);
-
- try {
- // Note: we must test against a real server, because the connection
- // gets established before the interceptor can crash the request.
- HttpGet method = new HttpGet(url);
- HttpResponse response = client.execute(method);
- } finally {
- client.close();
- }
- }
-
- private static class MockSession implements SSLSession {
-
- public String getPeerHost() {
- throw new UnsupportedOperationException();
- }
-
-
- public int getPeerPort() {
- throw new UnsupportedOperationException();
- }
-
-
-
- public int getApplicationBufferSize() {
- throw new UnsupportedOperationException();
- }
-
-
- public String getCipherSuite() {
- throw new UnsupportedOperationException();
- }
-
-
- public long getCreationTime() {
- throw new UnsupportedOperationException();
- }
-
-
- public byte[] getId() {
- throw new UnsupportedOperationException();
- }
-
-
- public long getLastAccessedTime() {
- throw new UnsupportedOperationException();
- }
-
-
- public Certificate[] getLocalCertificates() {
- throw new UnsupportedOperationException();
- }
-
-
- public Principal getLocalPrincipal() {
- throw new UnsupportedOperationException();
- }
-
-
- public int getPacketBufferSize() {
- throw new UnsupportedOperationException();
- }
-
-
- public X509Certificate[] getPeerCertificateChain()
- throws SSLPeerUnverifiedException {
- throw new UnsupportedOperationException();
- }
-
-
- public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
- throw new UnsupportedOperationException();
- }
-
-
- public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
- throw new UnsupportedOperationException();
- }
-
-
- public String getProtocol() {
- throw new UnsupportedOperationException();
- }
-
-
- public SSLSessionContext getSessionContext() {
- throw new UnsupportedOperationException();
- }
-
-
- public Object getValue(String name) {
- throw new UnsupportedOperationException();
- }
-
-
- public String[] getValueNames() {
- throw new UnsupportedOperationException();
- }
-
-
- public void invalidate() {
- throw new UnsupportedOperationException();
- }
-
-
- public boolean isValid() {
- throw new UnsupportedOperationException();
- }
-
-
- public void putValue(String name, Object value) {
- throw new UnsupportedOperationException();
- }
-
-
- public void removeValue(String name) {
- throw new UnsupportedOperationException();
- }
- }
-
-
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/GoogleHttpClientTest.java b/tests/AndroidTests/src/com/android/unit_tests/GoogleHttpClientTest.java
deleted file mode 100644
index bf0245c..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/GoogleHttpClientTest.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests;
-
-import android.content.ContentResolver;
-import android.provider.Checkin;
-import android.provider.Settings;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-
-import com.google.android.collect.Lists;
-import com.google.android.net.GoogleHttpClient;
-
-import org.apache.http.HttpException;
-import org.apache.http.HttpRequest;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.protocol.HttpContext;
-import org.apache.http.protocol.HttpRequestHandler;
-import org.apache.http.util.EntityUtils;
-
-import java.io.IOException;
-
-/** Unit test for {@link GoogleHttpClient}. */
-public class GoogleHttpClientTest extends AndroidTestCase {
- private TestHttpServer mServer;
- private String mServerUrl;
-
- protected void setUp() throws Exception {
- // Run a test server that echoes the URI back to the caller.
- mServer = new TestHttpServer();
- mServer.registerHandler("*", new HttpRequestHandler() {
- public void handle(
- HttpRequest request,
- HttpResponse response,
- HttpContext context) throws HttpException, IOException {
- String uri = request.getRequestLine().getUri();
- response.setEntity(new StringEntity(uri));
- }
- });
-
- mServer.start();
- mServerUrl = "http://localhost:" + mServer.getPort() + "/";
- }
-
- protected void tearDown() throws Exception {
- if (mServer != null) mServer.shutdown();
- }
-
- //
- // Fix this test to use the new mechanism to indicate that the
- // Http client is running in the UI thread
- // bug: http://b/2322326
- @LargeTest
- public void testThreadCheck() throws Exception {
- ContentResolver resolver = getContext().getContentResolver();
- GoogleHttpClient client = new GoogleHttpClient(resolver, "Test",
- false /* no gzip */);
-
- try {
- // Note: we must test against a real server, because the connection
- // gets established before the interceptor can crash the request.
- HttpGet method = new HttpGet(mServerUrl);
-
- // This is actually an AndroidHttpClient feature...
- // TODO: somehow test that Activity threads have the flag set?
- // Thus now uses the looper state to determine if it is in a UI
- // thread
- //AndroidHttpClient.setThreadBlocked(true);
-
- try {
- client.execute(method);
- fail("\"thread forbids HTTP requests\" exception expected");
- } catch (RuntimeException e) {
- if (!e.toString().contains("forbids HTTP requests")) throw e;
- } finally {
- // AndroidHttpClient.setThreadBlocked(false);
- }
-
- HttpResponse response = client.execute(method);
- assertEquals("/", EntityUtils.toString(response.getEntity()));
- } finally {
- client.close();
- }
- }
-
- @MediumTest
- public void testUrlRewriteRules() throws Exception {
- // Don't do anything exotic; UrlRulesTest checks the actual rewriter.
- // Just make sure that URLs are, in fact, rewritten.
-
- // TODO: Use a MockContentProvider/MockContentResolver instead.
- ContentResolver resolver = getContext().getContentResolver();
- GoogleHttpClient client = new GoogleHttpClient(resolver, "Test",
- false /* not gzip capable */);
- Settings.Gservices.putString(resolver,
- "url:test", "http://foo.bar/ rewrite " + mServerUrl + "new/");
-
- // Update the digest, so the UrlRules cache is reloaded.
- Settings.Gservices.putString(resolver, "digest", mServerUrl);
-
- try {
- HttpGet method = new HttpGet("http://foo.bar/path");
- HttpResponse response = client.execute(method);
- String body = EntityUtils.toString(response.getEntity());
- assertEquals("/new/path", body);
- } finally {
- client.close();
- }
- }
-}
diff --git a/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java b/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java
deleted file mode 100644
index a8af7f8..0000000
--- a/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java
+++ /dev/null
@@ -1,587 +0,0 @@
-package android.content;
-
-import com.google.android.collect.Lists;
-import com.google.android.collect.Sets;
-
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.test.AndroidTestCase;
-import android.text.TextUtils;
-import android.accounts.Account;
-
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.SortedSet;
-
-/** Unit test for {@link android.content.AbstractTableMerger}. */
-public class AbstractTableMergerTest extends AndroidTestCase {
- MockSyncableContentProvider mRealProvider;
- MockSyncableContentProvider mTempProvider;
- MockTableMerger mMerger;
- MockSyncContext mSyncContext;
-
- static final String TABLE_NAME = "items";
- static final String DELETED_TABLE_NAME = "deleted_items";
- static final Uri CONTENT_URI = Uri.parse("content://testdata");
- static final Uri TABLE_URI = Uri.withAppendedPath(CONTENT_URI, TABLE_NAME);
- static final Uri DELETED_TABLE_URI = Uri.withAppendedPath(CONTENT_URI, DELETED_TABLE_NAME);
-
- private final Account ACCOUNT = new Account("account@goo.com", "example.type");
-
- private final ArrayList<Expectation> mExpectations = Lists.newArrayList();
-
- static class Expectation {
- enum Type {
- UPDATE,
- INSERT,
- DELETE,
- RESOLVE
- }
-
- Type mType;
- ContentValues mValues;
- Long mLocalRowId;
-
- Expectation(Type type, Long localRowId, ContentValues values) {
- mType = type;
- mValues = values;
- mLocalRowId = localRowId;
- if (type == Type.DELETE) {
- assertNull(values);
- } else {
- assertFalse(values.containsKey("_id"));
- }
- }
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mSyncContext = new MockSyncContext();
- mRealProvider = new MockSyncableContentProvider();
- mTempProvider = mRealProvider.getTemporaryInstance();
- mMerger = new MockTableMerger(mRealProvider.getDatabase(),
- TABLE_NAME, TABLE_URI, DELETED_TABLE_NAME, DELETED_TABLE_URI);
- mExpectations.clear();
- }
-
- ContentValues newValues(String data, String syncId, Account syncAccount,
- String syncTime, String syncVersion, Long syncLocalId) {
- ContentValues values = new ContentValues();
- if (data != null) values.put("data", data);
- if (syncTime != null) values.put("_sync_time", syncTime);
- if (syncVersion != null) values.put("_sync_version", syncVersion);
- if (syncId != null) values.put("_sync_id", syncId);
- if (syncAccount != null) {
- values.put("_sync_account", syncAccount.name);
- values.put("_sync_account_type", syncAccount.type);
- }
- values.put("_sync_local_id", syncLocalId);
- values.put("_sync_dirty", 0);
- return values;
- }
-
- ContentValues newDeletedValues(String syncId, Account syncAccount, String syncVersion,
- Long syncLocalId) {
- ContentValues values = new ContentValues();
- if (syncVersion != null) values.put("_sync_version", syncVersion);
- if (syncId != null) values.put("_sync_id", syncId);
- if (syncAccount != null) {
- values.put("_sync_account", syncAccount.name);
- values.put("_sync_account_type", syncAccount.type);
- }
- if (syncLocalId != null) values.put("_sync_local_id", syncLocalId);
- return values;
- }
-
- ContentValues newModifyData(String data) {
- ContentValues values = new ContentValues();
- values.put("data", data);
- values.put("_sync_dirty", 1);
- return values;
- }
-
- // Want to test adding, changing, deleting entries to a provider that has extra entries
- // before and after the entries being changed.
- public void testInsert() {
- // add rows to the real provider
- // add new row to the temp provider
- final ContentValues row1 = newValues("d1", "si1", ACCOUNT, "st1", "sv1", null);
- mTempProvider.insert(TABLE_URI, row1);
-
- // add expected callbacks to merger
- mExpectations.add(new Expectation(Expectation.Type.INSERT, null /* syncLocalId */, row1));
-
- // run merger
- SyncResult syncResult = new SyncResult();
- mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
- // check that all expectations were met
- assertEquals("not all expectations were met", 0, mExpectations.size());
- }
-
- public void testUpdateWithLocalId() {
- // add rows to the real provider
- // add new row to the temp provider that matches an unsynced row in the real provider
- final ContentValues row1 = newValues("d1", "si1", ACCOUNT, "st1", "sv1", 11L);
- mTempProvider.insert(TABLE_URI, row1);
-
- // add expected callbacks to merger
- mExpectations.add(new Expectation(Expectation.Type.UPDATE, 11L, row1));
-
- // run merger
- SyncResult syncResult = new SyncResult();
- mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
- // check that all expectations were met
- assertEquals("not all expectations were met", 0, mExpectations.size());
- }
-
- public void testUpdateWithoutLocalId() {
- // add rows to the real provider
- Uri i1 = mRealProvider.insert(TABLE_URI,
- newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
-
- // add new row to the temp provider that matches an unsynced row in the real provider
- final ContentValues row1 = newValues("d2", "si1", ACCOUNT, "st2", "sv2", null);
- mTempProvider.insert(TABLE_URI, row1);
-
- // add expected callbacks to merger
- mExpectations.add(new Expectation(Expectation.Type.UPDATE, ContentUris.parseId(i1), row1));
-
- // run merger
- SyncResult syncResult = new SyncResult();
- mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
- // check that all expectations were met
- assertEquals("not all expectations were met", 0, mExpectations.size());
- }
-
- public void testResolve() {
- // add rows to the real provider
- Uri i1 = mRealProvider.insert(TABLE_URI,
- newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
- mRealProvider.update(TABLE_URI, newModifyData("d2"), null, null);
-
- // add row to the temp provider that matches a dirty, synced row in the real provider
- final ContentValues row1 = newValues("d3", "si1", ACCOUNT, "st2", "sv2", null);
- mTempProvider.insert(TABLE_URI, row1);
-
- // add expected callbacks to merger
- mExpectations.add(new Expectation(Expectation.Type.RESOLVE, ContentUris.parseId(i1), row1));
-
- // run merger
- SyncResult syncResult = new SyncResult();
- mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
- // check that all expectations were met
- assertEquals("not all expectations were met", 0, mExpectations.size());
- }
-
- public void testResolveWithLocalId() {
- // add rows to the real provider
- Uri i1 = mRealProvider.insert(TABLE_URI,
- newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
- mRealProvider.update(TABLE_URI, newModifyData("d2"), null, null);
-
- // add row to the temp provider that matches a dirty, synced row in the real provider
- ContentValues row1 = newValues("d2", "si1", ACCOUNT, "st2", "sv2", ContentUris.parseId(i1));
- mTempProvider.insert(TABLE_URI, row1);
-
- // add expected callbacks to merger
- mExpectations.add(new Expectation(Expectation.Type.UPDATE, ContentUris.parseId(i1), row1));
-
- // run merger
- SyncResult syncResult = new SyncResult();
- mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
- // check that all expectations were met
- assertEquals("not all expectations were met", 0, mExpectations.size());
- }
-
- public void testDeleteRowAfterDelete() {
- // add rows to the real provider
- Uri i1 = mRealProvider.insert(TABLE_URI,
- newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
-
- // add a deleted record to the temp provider
- ContentValues row1 = newDeletedValues(null, null, null, ContentUris.parseId(i1));
- mTempProvider.insert(DELETED_TABLE_URI, row1);
-
- // add expected callbacks to merger
- mExpectations.add(new Expectation(Expectation.Type.DELETE, ContentUris.parseId(i1), null));
-
- // run merger
- SyncResult syncResult = new SyncResult();
- mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
- // check that all expectations were met
- assertEquals("not all expectations were met", 0, mExpectations.size());
- }
-
- public void testDeleteRowAfterInsert() {
- // add rows to the real provider
- Uri i1 = mRealProvider.insert(TABLE_URI, newModifyData("d1"));
-
- // add a deleted record to the temp provider
- ContentValues row1 = newDeletedValues(null, null, null, ContentUris.parseId(i1));
- mTempProvider.insert(DELETED_TABLE_URI, row1);
-
- // add expected callbacks to merger
- mExpectations.add(new Expectation(Expectation.Type.DELETE, ContentUris.parseId(i1), null));
-
- // run merger
- SyncResult syncResult = new SyncResult();
- mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
- // check that all expectations were met
- assertEquals("not all expectations were met", 0, mExpectations.size());
- }
-
- public void testDeleteRowAfterUpdate() {
- // add rows to the real provider
- Uri i1 = mRealProvider.insert(TABLE_URI,
- newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
-
- // add a deleted record to the temp provider
- ContentValues row1 = newDeletedValues("si1", ACCOUNT, "sv1", ContentUris.parseId(i1));
- mTempProvider.insert(DELETED_TABLE_URI, row1);
-
- // add expected callbacks to merger
- mExpectations.add(new Expectation(Expectation.Type.DELETE, ContentUris.parseId(i1), null));
-
- // run merger
- SyncResult syncResult = new SyncResult();
- mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
- // check that all expectations were met
- assertEquals("not all expectations were met", 0, mExpectations.size());
- }
-
- public void testDeleteRowFromServer() {
- // add rows to the real provider
- Uri i1 = mRealProvider.insert(TABLE_URI,
- newValues("d1", "si1", ACCOUNT, "st1", "sv1", null));
-
- // add a deleted record to the temp provider
- ContentValues row1 = newDeletedValues("si1", ACCOUNT, "sv1", null);
- mTempProvider.insert(DELETED_TABLE_URI, row1);
-
- // add expected callbacks to merger
- mExpectations.add(new Expectation(Expectation.Type.DELETE, ContentUris.parseId(i1), null));
-
- // run merger
- SyncResult syncResult = new SyncResult();
- mMerger.mergeServerDiffs(mSyncContext, ACCOUNT, mTempProvider, syncResult);
-
- // check that all expectations were met
- assertEquals("not all expectations were met", 0, mExpectations.size());
- }
-
- class MockTableMerger extends AbstractTableMerger {
- public MockTableMerger(SQLiteDatabase database, String table, Uri tableURL,
- String deletedTable, Uri deletedTableURL) {
- super(database, table, tableURL, deletedTable, deletedTableURL);
- }
-
- public void insertRow(ContentProvider diffs, Cursor diffsCursor) {
- Expectation expectation = mExpectations.remove(0);
- checkExpectation(expectation,
- Expectation.Type.INSERT, null /* syncLocalId */, diffsCursor);
- }
-
- public void updateRow(long localPersonID, ContentProvider diffs, Cursor diffsCursor) {
- Expectation expectation = mExpectations.remove(0);
- checkExpectation(expectation, Expectation.Type.UPDATE, localPersonID, diffsCursor);
- }
-
- public void resolveRow(long localPersonID, String syncID, ContentProvider diffs,
- Cursor diffsCursor) {
- Expectation expectation = mExpectations.remove(0);
- checkExpectation(expectation, Expectation.Type.RESOLVE, localPersonID, diffsCursor);
- }
-
- @Override
- public void deleteRow(Cursor cursor) {
- Expectation expectation = mExpectations.remove(0);
- assertEquals(expectation.mType, Expectation.Type.DELETE);
- assertNotNull(expectation.mLocalRowId);
- final long localRowId = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
- assertEquals((long)expectation.mLocalRowId, localRowId);
- cursor.moveToNext();
- mDb.delete(TABLE_NAME, "_id=" + localRowId, null);
- }
-
- protected void notifyChanges() {
- throw new UnsupportedOperationException();
- }
-
- void checkExpectation(Expectation expectation,
- Expectation.Type actualType, Long localRowId,
- Cursor cursor) {
- assertEquals(expectation.mType, actualType);
- assertEquals(expectation.mLocalRowId, localRowId);
-
- final SortedSet<String> actualKeys = Sets.newSortedSet(cursor.getColumnNames());
- final SortedSet<String> expectedKeys = Sets.newSortedSet();
- for (Map.Entry<String, Object> entry : expectation.mValues.valueSet()) {
- expectedKeys.add(entry.getKey());
- }
- actualKeys.remove("_id");
- actualKeys.remove("_sync_mark");
- actualKeys.remove("_sync_local_id");
- expectedKeys.remove("_sync_local_id");
- expectedKeys.remove("_id");
- assertEquals("column mismatch",
- TextUtils.join(",", expectedKeys), TextUtils.join(",", actualKeys));
-
-// if (localRowId != null) {
-// assertEquals((long) localRowId,
-// cursor.getLong(cursor.getColumnIndexOrThrow("_sync_local_id")));
-// } else {
-// assertTrue("unexpected _sync_local_id, "
-// + cursor.getLong(cursor.getColumnIndexOrThrow("_sync_local_id")),
-// cursor.isNull(cursor.getColumnIndexOrThrow("_sync_local_id")));
-// }
-
- for (String name : cursor.getColumnNames()) {
- if ("_id".equals(name)) {
- continue;
- }
- if (cursor.isNull(cursor.getColumnIndexOrThrow(name))) {
- assertNull(expectation.mValues.getAsString(name));
- } else {
- String actualValue =
- cursor.getString(cursor.getColumnIndexOrThrow(name));
- assertEquals("mismatch on column " + name,
- expectation.mValues.getAsString(name), actualValue);
- }
- }
- }
- }
-
- class MockSyncableContentProvider extends SyncableContentProvider {
- SQLiteDatabase mDb;
- boolean mIsTemporary;
- boolean mContainsDiffs;
-
- private final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
-
- private static final int MATCHER_ITEMS = 0;
- private static final int MATCHER_DELETED_ITEMS = 1;
-
- public MockSyncableContentProvider() {
- mIsTemporary = false;
- setContainsDiffs(false);
- sURIMatcher.addURI(CONTENT_URI.getAuthority(), "items", MATCHER_ITEMS);
- sURIMatcher.addURI(CONTENT_URI.getAuthority(), "deleted_items", MATCHER_DELETED_ITEMS);
-
- mDb = SQLiteDatabase.create(null);
- mDb.execSQL("CREATE TABLE items ("
- + "_id INTEGER PRIMARY KEY AUTOINCREMENT, "
- + "data TEXT, "
- + "_sync_time TEXT, "
- + "_sync_version TEXT, "
- + "_sync_id TEXT, "
- + "_sync_local_id INTEGER, "
- + "_sync_dirty INTEGER NOT NULL DEFAULT 0, "
- + "_sync_account TEXT, "
- + "_sync_account_type TEXT, "
- + "_sync_mark INTEGER)");
-
- mDb.execSQL("CREATE TABLE deleted_items ("
- + "_id INTEGER PRIMARY KEY AUTOINCREMENT, "
- + "_sync_version TEXT, "
- + "_sync_id TEXT, "
- + "_sync_local_id INTEGER, "
- + "_sync_account TEXT, "
- + "_sync_account_type TEXT, "
- + "_sync_mark INTEGER)");
- }
-
- public boolean onCreate() {
- throw new UnsupportedOperationException();
- }
-
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- int match = sURIMatcher.match(uri);
- switch (match) {
- case MATCHER_ITEMS:
- return mDb.query(TABLE_NAME, projection, selection, selectionArgs,
- null, null, sortOrder);
- case MATCHER_DELETED_ITEMS:
- return mDb.query(DELETED_TABLE_NAME, projection, selection, selectionArgs,
- null, null, sortOrder);
- default:
- throw new UnsupportedOperationException("Cannot query URL: " + uri);
- }
- }
-
- public String getType(Uri uri) {
- throw new UnsupportedOperationException();
- }
-
- public Uri insert(Uri uri, ContentValues values) {
- int match = sURIMatcher.match(uri);
- switch (match) {
- case MATCHER_ITEMS: {
- long id = mDb.insert(TABLE_NAME, "_id", values);
- return CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build();
- }
- case MATCHER_DELETED_ITEMS: {
- long id = mDb.insert(DELETED_TABLE_NAME, "_id", values);
- return CONTENT_URI.buildUpon().appendPath(String.valueOf(id)).build();
- }
- default:
- throw new UnsupportedOperationException("Cannot query URL: " + uri);
- }
- }
-
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- int match = sURIMatcher.match(uri);
- switch (match) {
- case MATCHER_ITEMS:
- return mDb.delete(TABLE_NAME, selection, selectionArgs);
- case MATCHER_DELETED_ITEMS:
- return mDb.delete(DELETED_TABLE_NAME, selection, selectionArgs);
- default:
- throw new UnsupportedOperationException("Cannot query URL: " + uri);
- }
- }
-
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- int match = sURIMatcher.match(uri);
- switch (match) {
- case MATCHER_ITEMS:
- return mDb.update(TABLE_NAME, values, selection, selectionArgs);
- case MATCHER_DELETED_ITEMS:
- return mDb.update(DELETED_TABLE_NAME, values, selection, selectionArgs);
- default:
- throw new UnsupportedOperationException("Cannot query URL: " + uri);
- }
- }
-
- protected boolean isTemporary() {
- return mIsTemporary;
- }
-
- public void close() {
- throw new UnsupportedOperationException();
- }
-
- protected void bootstrapDatabase(SQLiteDatabase db) {
- throw new UnsupportedOperationException();
- }
-
- protected boolean upgradeDatabase(SQLiteDatabase db, int oldVersion, int newVersion) {
- throw new UnsupportedOperationException();
- }
-
- protected void onDatabaseOpened(SQLiteDatabase db) {
- throw new UnsupportedOperationException();
- }
-
- public MockSyncableContentProvider getTemporaryInstance() {
- MockSyncableContentProvider temp = new MockSyncableContentProvider();
- temp.mIsTemporary = true;
- temp.setContainsDiffs(true);
- return temp;
- }
-
- public SQLiteDatabase getDatabase() {
- return mDb;
- }
-
- public boolean getContainsDiffs() {
- return mContainsDiffs;
- }
-
- public void setContainsDiffs(boolean containsDiffs) {
- mContainsDiffs = containsDiffs;
- }
-
- protected Iterable<? extends AbstractTableMerger> getMergers() {
- throw new UnsupportedOperationException();
- }
-
- public boolean changeRequiresLocalSync(Uri uri) {
- throw new UnsupportedOperationException();
- }
-
- public void onSyncStart(SyncContext context, Account account) {
- throw new UnsupportedOperationException();
- }
-
- public void onSyncStop(SyncContext context, boolean success) {
- throw new UnsupportedOperationException();
- }
-
- public Account getSyncingAccount() {
- throw new UnsupportedOperationException();
- }
-
- public void merge(SyncContext context, SyncableContentProvider diffs,
- TempProviderSyncResult result, SyncResult syncResult) {
- throw new UnsupportedOperationException();
- }
-
- public void onSyncCanceled() {
- throw new UnsupportedOperationException();
- }
-
- public boolean isMergeCancelled() {
- return false;
- }
-
- protected int updateInternal(Uri url, ContentValues values, String selection,
- String[] selectionArgs) {
- throw new UnsupportedOperationException();
- }
-
- protected int deleteInternal(Uri url, String selection, String[] selectionArgs) {
- throw new UnsupportedOperationException();
- }
-
- protected Uri insertInternal(Uri url, ContentValues values) {
- throw new UnsupportedOperationException();
- }
-
- protected Cursor queryInternal(Uri url, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
- throw new UnsupportedOperationException();
- }
-
- protected void onAccountsChanged(Account[] accountsArray) {
- throw new UnsupportedOperationException();
- }
-
- protected void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts, String table
- ) {
- throw new UnsupportedOperationException();
- }
-
- public void wipeAccount(Account account) {
- throw new UnsupportedOperationException();
- }
-
- public byte[] readSyncDataBytes(Account account) {
- throw new UnsupportedOperationException();
- }
-
- public void writeSyncDataBytes(Account account, byte[] data) {
- throw new UnsupportedOperationException();
- }
- }
-
- class MockSyncContext extends SyncContext {
- public MockSyncContext() {
- super(null);
- }
-
- @Override
- public void setStatusText(String message) {
- }
- }
-}