Merge "unhide audio pre processing API."
diff --git a/api/current.txt b/api/current.txt
index db171e2..afcca224 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3768,7 +3768,6 @@
method public android.app.Notification.Builder setDefaults(int);
method public android.app.Notification.Builder setDeleteIntent(android.app.PendingIntent);
method public android.app.Notification.Builder setFullScreenIntent(android.app.PendingIntent, boolean);
- method public android.app.Notification.Builder setIntruderActionsShowText(boolean);
method public android.app.Notification.Builder setLargeIcon(android.graphics.Bitmap);
method public android.app.Notification.Builder setLights(int, int, int);
method public android.app.Notification.Builder setNumber(int);
@@ -3784,7 +3783,6 @@
method public android.app.Notification.Builder setTicker(java.lang.CharSequence);
method public android.app.Notification.Builder setTicker(java.lang.CharSequence, android.widget.RemoteViews);
method public android.app.Notification.Builder setUsesChronometer(boolean);
- method public android.app.Notification.Builder setUsesIntruderAlert(boolean);
method public android.app.Notification.Builder setVibrate(long[]);
method public android.app.Notification.Builder setWhen(long);
}
@@ -17861,9 +17859,11 @@
field public static final java.lang.String DATE_ADDED = "date_added";
field public static final java.lang.String DATE_MODIFIED = "date_modified";
field public static final java.lang.String DISPLAY_NAME = "_display_name";
+ field public static final java.lang.String HEIGHT = "height";
field public static final java.lang.String MIME_TYPE = "mime_type";
field public static final java.lang.String SIZE = "_size";
field public static final java.lang.String TITLE = "title";
+ field public static final java.lang.String WIDTH = "width";
}
public static final class MediaStore.Video {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 22d84f0..b581f99 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -190,12 +190,6 @@
public RemoteViews contentView;
/**
- * The view that will represent this notification in the pop-up "intruder alert" dialog.
- * @hide
- */
- public RemoteViews intruderView;
-
- /**
* A large-format version of {@link #contentView}, giving the Notification an
* opportunity to show more detail. The system UI may choose to show this
* instead of the normal content view at its discretion.
@@ -590,9 +584,6 @@
actions = parcel.createTypedArray(Action.CREATOR);
if (parcel.readInt() != 0) {
- intruderView = RemoteViews.CREATOR.createFromParcel(parcel);
- }
- if (parcel.readInt() != 0) {
bigContentView = RemoteViews.CREATOR.createFromParcel(parcel);
}
}
@@ -658,9 +649,6 @@
for(int i=0; i<this.actions.length; i++) {
that.actions[i] = this.actions[i].clone();
}
- if (this.intruderView != null) {
- that.intruderView = this.intruderView.clone();
- }
if (this.bigContentView != null) {
that.bigContentView = this.bigContentView.clone();
}
@@ -755,13 +743,6 @@
parcel.writeTypedArray(actions, 0);
- if (intruderView != null) {
- parcel.writeInt(1);
- intruderView.writeToParcel(parcel, 0);
- } else {
- parcel.writeInt(0);
- }
-
if (bigContentView != null) {
parcel.writeInt(1);
bigContentView.writeToParcel(parcel, 0);
@@ -942,8 +923,6 @@
private Bundle mExtras;
private int mPriority;
private ArrayList<Action> mActions = new ArrayList<Action>(3);
- private boolean mCanHasIntruder;
- private boolean mIntruderActionsShowText;
private boolean mUseChronometer;
/**
@@ -1349,38 +1328,6 @@
return this;
}
- /**
- * Specify whether this notification should pop up as an
- * "intruder alert" (a small window that shares the screen with the
- * current activity). This sort of notification is (as the name implies)
- * very intrusive, so use it sparingly for notifications that require
- * the user's attention.
- *
- * Notes:
- * <ul>
- * <li>Intruder alerts only show when the screen is on.</li>
- * <li>Intruder alerts take precedence over fullScreenIntents.</li>
- * </ul>
- *
- * @param intrude Whether to pop up an intruder alert (default false).
- */
- public Builder setUsesIntruderAlert(boolean intrude) {
- mCanHasIntruder = intrude;
- return this;
- }
-
- /**
- * Control text on intruder alert action buttons. By default, action
- * buttons in intruders do not show textual labels.
- *
- * @param showActionText Whether to show text labels beneath action
- * icons (default false).
- */
- public Builder setIntruderActionsShowText(boolean showActionText) {
- mIntruderActionsShowText = showActionText;
- return this;
- }
-
private void setFlag(int mask, boolean value) {
if (value) {
mFlags |= mask;
@@ -1506,45 +1453,6 @@
return applyStandardTemplateWithActions(R.layout.notification_template_base);
}
- private RemoteViews makeIntruderView(boolean showLabels) {
- RemoteViews intruderView = new RemoteViews(mContext.getPackageName(),
- R.layout.notification_intruder_content);
- if (mLargeIcon != null) {
- intruderView.setImageViewBitmap(R.id.icon, mLargeIcon);
- intruderView.setViewVisibility(R.id.icon, View.VISIBLE);
- } else if (mSmallIcon != 0) {
- intruderView.setImageViewResource(R.id.icon, mSmallIcon);
- intruderView.setViewVisibility(R.id.icon, View.VISIBLE);
- } else {
- intruderView.setViewVisibility(R.id.icon, View.GONE);
- }
- if (mContentTitle != null) {
- intruderView.setTextViewText(R.id.title, mContentTitle);
- }
- if (mContentText != null) {
- intruderView.setTextViewText(R.id.text, mContentText);
- }
- if (mActions.size() > 0) {
- intruderView.setViewVisibility(R.id.actions, View.VISIBLE);
- int N = mActions.size();
- if (N>3) N=3;
- final int[] BUTTONS = { R.id.action0, R.id.action1, R.id.action2 };
- for (int i=0; i<N; i++) {
- final Action action = mActions.get(i);
- final int buttonId = BUTTONS[i];
-
- intruderView.setViewVisibility(buttonId, View.VISIBLE);
- intruderView.setTextViewText(buttonId, showLabels ? action.title : null);
- intruderView.setTextViewCompoundDrawables(buttonId, 0, action.icon, 0, 0);
- intruderView.setContentDescription(buttonId, action.title);
- intruderView.setOnClickPendingIntent(buttonId, action.actionIntent);
- }
- } else {
- intruderView.setViewVisibility(R.id.actions, View.GONE);
- }
- return intruderView;
- }
-
private RemoteViews generateActionButton(Action action) {
RemoteViews button = new RemoteViews(mContext.getPackageName(), R.layout.notification_action);
button.setTextViewCompoundDrawables(R.id.action0, action.icon, 0, 0, 0);
@@ -1579,9 +1487,6 @@
n.ledOffMS = mLedOffMs;
n.defaults = mDefaults;
n.flags = mFlags;
- if (mCanHasIntruder) {
- n.intruderView = makeIntruderView(mIntruderActionsShowText);
- }
n.bigContentView = makeBigContentView();
if (mLedOnMs != 0 && mLedOffMs != 0) {
n.flags |= FLAG_SHOW_LIGHTS;
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 640b47b..4fb710e 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -234,48 +234,6 @@
};
/**
- * Creates a new Camera object to access a particular hardware camera.
- *
- * <p>When <code>force</code> is set to false, this will throw an exception
- * if the same camera is already opened by other clients. If true, the other
- * client will be disconnected from the camera they opened. If the device
- * can only support one camera running at a time, all camera-using clients
- * will be disconnected from their cameras.
- *
- * <p>A camera being held by an application can be taken away by other
- * applications at any time. Before the camera is taken, applications will
- * get {@link #CAMERA_ERROR_RELEASED} and have some time to clean up. Apps
- * receiving this callback must immediately stop video recording and then
- * call {@link #release()} on their camera object. Otherwise, it will be
- * released by the frameworks in a short time. After receiving
- * CAMERA_ERROR_RELEASED, apps should not call any method except <code>
- * release</code> and {@link #isReleased()}. After a camera is taken away,
- * all methods will throw exceptions except <code>isReleased</code> and
- * <code>release</code>. Apps can use <code>isReleased</code> to see if the
- * camera has been taken away. If the camera is taken away, the apps can
- * silently finish themselves or show a dialog.
- *
- * <p>Applications with android.permission.KEEP_CAMERA can request to keep
- * the camera. That is, the camera will not be taken by other applications
- * while it is opened. The permission can only be obtained by trusted
- * platform applications, such as those implementing lock screen security
- * features.
- *
- * @param cameraId the hardware camera to access, between 0 and
- * {@link #getNumberOfCameras()}-1.
- * @param force true to take the ownership from the existing client if the
- * camera has been opened by other clients.
- * @param keep true if the applications do not want other apps to take the
- * camera. Only the apps with android.permission.KEEP_CAMERA can keep
- * the camera.
- * @return a new Camera object, connected, locked and ready for use.
- * @hide
- */
- public static Camera open(int cameraId, boolean force, boolean keep) {
- return new Camera(cameraId, force, keep);
- }
-
- /**
* Creates a new Camera object to access a particular hardware camera. If
* the same camera is opened by other applications, this will throw a
* RuntimeException.
@@ -305,7 +263,7 @@
* @see android.app.admin.DevicePolicyManager#getCameraDisabled(android.content.ComponentName)
*/
public static Camera open(int cameraId) {
- return new Camera(cameraId, false, false);
+ return new Camera(cameraId);
}
/**
@@ -320,13 +278,13 @@
for (int i = 0; i < numberOfCameras; i++) {
getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
- return new Camera(i, false, false);
+ return new Camera(i);
}
}
return null;
}
- Camera(int cameraId, boolean force, boolean keep) {
+ Camera(int cameraId) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
@@ -343,7 +301,7 @@
mEventHandler = null;
}
- native_setup(new WeakReference<Camera>(this), cameraId, force, keep);
+ native_setup(new WeakReference<Camera>(this), cameraId);
}
/**
@@ -356,8 +314,7 @@
release();
}
- private native final void native_setup(Object camera_this, int cameraId,
- boolean force, boolean keep);
+ private native final void native_setup(Object camera_this, int cameraId);
private native final void native_release();
@@ -372,18 +329,6 @@
}
/**
- * Whether the camera is released. When any camera method throws an
- * exception, applications can use this to check whether the camera has been
- * taken by other clients. If true, it means other clients have taken the
- * camera. The applications can silently finish themselves or show a dialog.
- *
- * @return whether the camera is released.
- * @see #open(int, boolean, boolean)
- * @hide
- */
- public native final boolean isReleased();
-
- /**
* Unlocks the camera to allow another process to access it.
* Normally, the camera is locked to the process with an active Camera
* object until {@link #release()} is called. To allow rapid handoff
@@ -1377,17 +1322,6 @@
public static final int CAMERA_ERROR_UNKNOWN = 1;
/**
- * Camera was released because another client has opened the camera. The
- * application should call {@link #release()} after getting this. The apps
- * should not call any method except <code>release</code> and {@link #isReleased()}
- * after this.
- *
- * @see Camera.ErrorCallback
- * @hide
- */
- public static final int CAMERA_ERROR_RELEASED = 2;
-
- /**
* Media server died. In this case, the application must release the
* Camera object and instantiate a new one.
* @see Camera.ErrorCallback
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 38945c2..79d0144 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -306,13 +306,11 @@
/**
* The width of the image/video in pixels.
- * @hide
*/
public static final String WIDTH = "width";
/**
* The height of the image/video in pixels.
- * @hide
*/
public static final String HEIGHT = "height";
}
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 36b4b45..6cd8955 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -457,9 +457,9 @@
// connect to camera service
static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
- jobject weak_this, jint cameraId, jboolean force, jboolean keep)
+ jobject weak_this, jint cameraId)
{
- sp<Camera> camera = Camera::connect(cameraId, force, keep);
+ sp<Camera> camera = Camera::connect(cameraId);
if (camera == NULL) {
jniThrowRuntimeException(env, "Fail to connect to camera service");
@@ -821,15 +821,6 @@
}
}
-static bool android_hardware_Camera_isReleased(JNIEnv *env, jobject thiz)
-{
- ALOGV("isReleased");
- sp<Camera> camera = get_native_camera(env, thiz, NULL);
- if (camera == 0) return true;
-
- return (camera->sendCommand(CAMERA_CMD_PING, 0, 0) != NO_ERROR);
-}
-
//-------------------------------------------------
static JNINativeMethod camMethods[] = {
@@ -840,7 +831,7 @@
"(ILandroid/hardware/Camera$CameraInfo;)V",
(void*)android_hardware_Camera_getCameraInfo },
{ "native_setup",
- "(Ljava/lang/Object;IZZ)V",
+ "(Ljava/lang/Object;I)V",
(void*)android_hardware_Camera_native_setup },
{ "native_release",
"()V",
@@ -908,9 +899,6 @@
{ "enableFocusMoveCallback",
"(I)V",
(void *)android_hardware_Camera_enableFocusMoveCallback},
- { "isReleased",
- "()Z",
- (void *)android_hardware_Camera_isReleased},
};
struct field {
diff --git a/core/res/res/layout/notification_template_base.xml b/core/res/res/layout/notification_template_base.xml
index 74b3fa9..bd26232 100644
--- a/core/res/res/layout/notification_template_base.xml
+++ b/core/res/res/layout/notification_template_base.xml
@@ -19,7 +19,7 @@
android:background="@android:color/background_dark"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="64dp"
internal:layout_minHeight="64dp"
internal:layout_maxHeight="64dp"
>
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index 686bde3..a21708c 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -124,6 +124,23 @@
</li>
<li class="toggle-list">
+ <div><a href="<?cs var:toroot ?>training/cloudsync/index.html">
+ <span class="en">Syncing to the Cloud<span class="new"> new!</span></span>
+ </a></div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/cloudsync/aesync.html">
+ <span class="en">Syncing with App Engine</span>
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/cloudsync/backupapi.html">
+ <span class="en">Using the Backup API</span>
+ </a>
+ </li>
+ </ul>
+ </li>
+
+
+ <li class="toggle-list">
<div><a href="<?cs var:toroot ?>training/search/index.html">
<span class="en">Adding Search Functionality<span class="new"> new!</span></span>
</a>
@@ -369,11 +386,11 @@
</li>
<li><a href="<?cs var:toroot ?>training/displaying-bitmaps/display-bitmap.html">
<span class="en">Displaying Bitmaps in Your UI</span>
- </a>
</li>
- </ul>
+ <ul>
</li>
+
<li class="toggle-list">
<div><a href="<?cs var:toroot ?>training/accessibility/index.html">
<span class="en">Implementing Accessibility<span class="new"> new!</span></span>
@@ -391,9 +408,11 @@
</li>
</ul>
- </li>
-
-
+ </li>
+
+
+
+
<li>
<span class="heading">
<span class="en">Technical Resources</span>
diff --git a/docs/html/training/location/currentlocation.jd b/docs/html/training/basics/location/currentlocation.jd
similarity index 100%
rename from docs/html/training/location/currentlocation.jd
rename to docs/html/training/basics/location/currentlocation.jd
diff --git a/docs/html/training/location/geocoding.jd b/docs/html/training/basics/location/geocoding.jd
similarity index 100%
rename from docs/html/training/location/geocoding.jd
rename to docs/html/training/basics/location/geocoding.jd
diff --git a/docs/html/training/location/index.jd b/docs/html/training/basics/location/index.jd
similarity index 100%
rename from docs/html/training/location/index.jd
rename to docs/html/training/basics/location/index.jd
diff --git a/docs/html/training/location/locationmanager.jd b/docs/html/training/basics/location/locationmanager.jd
similarity index 100%
rename from docs/html/training/location/locationmanager.jd
rename to docs/html/training/basics/location/locationmanager.jd
diff --git a/docs/html/training/cloudsync/aesync.jd b/docs/html/training/cloudsync/aesync.jd
new file mode 100644
index 0000000..c60d28b
--- /dev/null
+++ b/docs/html/training/cloudsync/aesync.jd
@@ -0,0 +1,432 @@
+page.title=Syncing with App Engine
+parent.title=Syncing to the Cloud
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Using the Backup API
+next.link=backupapi.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you how to</h2>
+<ol>
+ <li><a href="#prepare">Prepare Your Environment</a></li>
+ <li><a href="#project">Create Your Project</a></li>
+ <li><a href="#data">Create the Data Layer</a></li>
+ <li><a href="#persistence">Create the Persistence Layer</a></li>
+ <li><a href="#androidapp">Query and Update from the Android App</a></li>
+ <li><a href="#serverc2dm">Configure the C2DM Server-Side</a></li>
+ <li><a href="#clientc2dm">Configure the C2DM Client-Side</a></li>
+</ol>
+<h2>You should also read</h2>
+ <ul>
+ <li><a
+ href="http://developers.google.com/appengine/">App Engine</a></li>
+ <li><a href="http://code.google.com/android/c2dm/">Android Cloud to Device
+ Messaging Framework</a></li>
+ </ul>
+<h2>Try it out</h2>
+
+<p>This lesson uses the Cloud Tasks sample code, originally shown at the
+<a href="http://www.youtube.com/watch?v=M7SxNNC429U">Android + AppEngine: A Developer's Dream Combination</a>
+talk at Google I/O. You can use the sample application as a source of reusable code for your own
+application, or simply as a reference for how the Android and cloud pieces of the overall
+application fit together. You can also build the sample application and see how it runs
+on your own device or emulator.</p>
+<p>
+ <a href="http://code.google.com/p/cloud-tasks-io/" class="button">Cloud Tasks
+ App</a>
+</p>
+
+</div>
+</div>
+
+<p>Writing an app that syncs to the cloud can be a challenge. There are many little
+details to get right, like server-side auth, client-side auth, a shared data
+model, and an API. One way to make this much easier is to use the Google Plugin
+for Eclipse, which handles a lot of the plumbing for you when building Android
+and App Engine applications that talk to each other. This lesson walks you through building such a project.</p>
+
+<p>Following this lesson shows you how to:</p>
+<ul>
+ <li>Build Android and Appengine apps that can communicate with each other</li>
+ <li>Take advantage of Cloud to Device Messaging (C2DM) so your Android app doesn't have to poll for updates</li>
+</ul>
+
+<p>This lesson focuses on local development, and does not cover distribution
+(i.e, pushing your App Engine app live, or publishing your Android App to
+market), as those topics are covered extensively elsewhere.</p>
+
+<h2 id="prepare">Prepare Your Environment</h2>
+<p>If you want to follow along with the code example in this lesson, you must do
+the following to prepare your development environment:</p>
+<ul>
+<li>Install the <a href="http://code.google.com/eclipse/">Google Plugin for
+ Eclipse.</a></li>
+<li>Install the <a
+ href="http://code.google.com/webtoolkit/download.html">GWT SDK</a> and the <a
+ href="http://code.google.com/appengine/">Java App Engine SDK</a>. The <a
+ href="http://code.google.com/eclipse/docs/getting_started.html">Quick Start
+ Guide</a> shows you how to install these components.</li>
+<li>Sign up for <a href="http://code.google.com/android/c2dm/signup.html">C2DM
+ access</a>. We strongly recommend <a
+ href="https://accounts.google.com/SignUp">creating a new Google account</a> specifically for
+connecting to C2DM. The server component in this lesson uses this <em>role
+ account</em> repeatedly to authenticate with Google servers.
+</li>
+</ul>
+
+<h2 id="project">Create Your Projects</h2>
+<p>After installing the Google Plugin for Eclipse, notice that a new kind of Android project
+exists when you create a new Eclipse project: The <strong>App Engine Connected
+ Android Project</strong> (under the <strong>Google</strong> project category).
+A wizard guides you through creating this project,
+during the course of which you are prompted to enter the account credentials for the role
+account you created.</p>
+
+<p class="note"><strong>Note:</strong> Remember to enter the credentials for
+your <i>role account</i> (the one you created to access C2DM services), not an
+account you'd log into as a user, or as an admin.</p>
+
+<p>Once you're done, you'll see two projects waiting for you in your
+workspace—An Android application and an App Engine application. Hooray!
+These two applications are already fully functional— the wizard has
+created a sample application which lets you authenticate to the App Engine
+application from your Android device using AccountManager (no need to type in
+your credentials), and an App Engine app that can send messages to any logged-in
+device using C2DM. In order to spin up your application and take it for a test
+drive, do the following:</p>
+
+<p>To spin up the Android application, make sure you have an AVD with a platform
+version of <em>at least</em> Android 2.2 (API Level 8). Right click on the Android project in
+Eclipse, and go to <strong>Debug As > Local App Engine Connected Android
+ Application</strong>. This launches the emulator in such a way that it can
+test C2DM functionality (which typically works through Google Play). It'll
+also launch a local instance of App Engine containing your awesome
+application.</p>
+
+<h2 id="data">Create the Data Layer</h2>
+
+<p>At this point you have a fully functional sample application running. Now
+it's time to start changing the code to create your own application.</p>
+
+<p>First, create the data model that defines the data shared between
+the App Engine and Android applications. To start, open up the source folder of
+your App Engine project, and navigate down to the <strong>(yourApp)-AppEngine
+ > src > (yourapp) > server</strong> package. Create a new class in there containing some data you want to
+store server-side. The code ends up looking something like this:</p>
+<pre>
+package com.cloudtasks.server;
+
+import javax.persistence.*;
+
+@Entity
+public class Task {
+
+ private String emailAddress;
+ private String name;
+ private String userId;
+ private String note;
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ public Task() {
+ }
+
+ public String getEmailAddress() {
+ return this.emailAddress;
+ }
+
+ public Long getId() {
+ return this.id;
+ }
+ ...
+}
+</pre>
+<p>Note the use of annotations: <code>Entity</code>, <code>Id</code> and
+<code>GeneratedValue</code> are all part of the <a
+ href="http://www.oracle.com/technetwork/articles/javaee/jpa-137156.html">Java
+ Persistence API</a>. Essentially, the <code>Entity</code> annotation goes
+above the class declaration, and indicates that this class represents an entity
+in your data layer. The <code>Id</code> and <code>GeneratedValue</code>
+annotations, respectively, indicate the field used as a lookup key for this
+class, and how that id is generated (in this case,
+<code>GenerationType.IDENTITY</code> indicates that the is generated by
+the database). You can find more on this topic in the App Engine documentation,
+on the page <a
+ href="http://code.google.com/appengine/docs/java/datastore/jpa/overview.html">Using
+ JPA with App Engine</a>.</p>
+
+<p>Once you've written all the classes that represent entities in your data
+layer, you need a way for the Android and App Engine applications to communicate
+about this data. This communication is enabled by creating a Remote Procedure
+Call (RPC) service.
+Typically, this involves a lot of monotonous code. Fortunately, there's an easy way! Right
+click on the server project in your App Engine source folder, and in the context
+menu, navigate to <strong>New > Other</strong> and then, in the resulting
+screen, select <strong>Google > RPC Service.</strong> A wizard appears, pre-populated
+with all the Entities you created in the previous step,
+which it found by seeking out the <code>@Entity</code> annotation in the
+source files you added. Pretty neat, right? Click <strong>Finish</strong>, and the wizard
+creates a Service class with stub methods for the Create, Retrieve, Update and
+Delete (CRUD) operations of all your entities.</p>
+
+<h2 id="persistence">Create the Persistence Layer</h2>
+
+<p>The persistence layer is where your application data is stored
+long-term, so any information you want to keep for your users needs to go here.
+You have several options for writing your persistence layer, depending on
+what kind of data you want to store. A few of the options hosted by Google
+(though you don't have to use these services) include <a
+ href="http://code.google.com/apis/storage/">Google Storage for Developers</a>
+and App Engine's built-in <a
+ href="http://code.google.com/appengine/docs/java/gettingstarted/usingdatastore.html">Datastore</a>.
+The sample code for this lesson uses DataStore code.</p>
+
+<p>Create a class in your <code>com.cloudtasks.server</code> package to handle
+persistence layer input and output. In order to access the data store, use the <a
+ href="http://db.apache.org/jdo/api20/apidocs/javax/jdo/PersistenceManager.html">PersistenceManager</a>
+class. You can generate an instance of this class using the PMF class in the
+<code>com.google.android.c2dm.server.PMF</code> package, and then use that to
+perform basic CRUD operations on your data store, like this:</p>
+<pre>
+/**
+* Remove this object from the data store.
+*/
+public void delete(Long id) {
+ PersistenceManager pm = PMF.get().getPersistenceManager();
+ try {
+ Task item = pm.getObjectById(Task.class, id);
+ pm.deletePersistent(item);
+ } finally {
+ pm.close();
+ }
+}
+</pre>
+
+<p>You can also use <a
+ href="http://code.google.com/appengine/docs/python/datastore/queryclass.html">Query</a>
+objects to retrieve data from your Datastore. Here's an example of a method
+that searches out an object by its ID.</p>
+
+<pre>
+public Task find(Long id) {
+ if (id == null) {
+ return null;
+ }
+
+ PersistenceManager pm = PMF.get().getPersistenceManager();
+ try {
+ Query query = pm.newQuery("select from " + Task.class.getName()
+ + " where id==" + id.toString() + " && emailAddress=='" + getUserEmail() + "'");
+ List<Task> list = (List<Task>) query.execute();
+ return list.size() == 0 ? null : list.get(0);
+ } catch (RuntimeException e) {
+ System.out.println(e);
+ throw e;
+ } finally {
+ pm.close();
+ }
+}
+</pre>
+
+<p>For a good example of a class that encapsulates the persistence layer for
+you, check out the <a
+ href="http://code.google.com/p/cloud-tasks-io/source/browse/trunk/CloudTasks-AppEngine/src/com/cloudtasks/server/DataStore.java">DataStore</a>
+class in the Cloud Tasks app.</p>
+
+
+
+<h2 id="androidapp">Query and Update from the Android App</h2>
+
+<p>In order to keep in sync with the App Engine application, your Android application
+needs to know how to do two things: Pull data from the cloud, and send data up
+to the cloud. Much of the plumbing for this is generated by the
+plugin, but you need to wire it up to your Android user interface yourself.</p>
+
+<p>Pop open the source code for the main Activity in your project and look for
+<code><YourProjectName> Activity.java</code>, then for the method
+<code>setHelloWorldScreenContent()</code>. Obviously you're not building a
+HelloWorld app, so delete this method entirely and replace it
+with something relevant. However, the boilerplate code has some very important
+characteristics. For one, the code that communicates with the cloud is wrapped
+in an {@link android.os.AsyncTask} and therefore <em>not</em> hitting the
+network on the UI thread. Also, it gives an easy template for how to access
+the cloud in your own code, using the <a
+ href="http://code.google.com/webtoolkit/doc/latest/DevGuideRequestFactory.html">RequestFactory</a>
+class generated that was auto-generated for you by the Eclipse plugin (called
+MyRequestFactory in the example below), and various {@code Request} types.</p>
+
+<p>For instance, if your server-side data model included an object called {@code
+Task} when you generated an RPC layer it automatically created a
+{@code TaskRequest} class for you, as well as a {@code TaskProxy} representing the individual
+task. In code, requesting a list of all these tasks from the server looks
+like this:</p>
+
+<pre>
+public void fetchTasks (Long id) {
+ // Request is wrapped in an AsyncTask to avoid making a network request
+ // on the UI thread.
+ new AsyncTask<Long, Void, List<TaskProxy>>() {
+ @Override
+ protected List<TaskProxy> doInBackground(Long... arguments) {
+ final List<TaskProxy> list = new ArrayList<TaskProxy>();
+ MyRequestFactory factory = Util.getRequestFactory(mContext,
+ MyRequestFactory.class);
+ TaskRequest taskRequest = factory.taskNinjaRequest();
+
+ if (arguments.length == 0 || arguments[0] == -1) {
+ factory.taskRequest().queryTasks().fire(new Receiver<List<TaskProxy>>() {
+ @Override
+ public void onSuccess(List<TaskProxy> arg0) {
+ list.addAll(arg0);
+ }
+ });
+ } else {
+ newTask = true;
+ factory.taskRequest().readTask(arguments[0]).fire(new Receiver<TaskProxy>() {
+ @Override
+ public void onSuccess(TaskProxy arg0) {
+ list.add(arg0);
+ }
+ });
+ }
+ return list;
+ }
+
+ @Override
+ protected void onPostExecute(List<TaskProxy> result) {
+ TaskNinjaActivity.this.dump(result);
+ }
+
+ }.execute(id);
+}
+...
+
+public void dump (List<TaskProxy> tasks) {
+ for (TaskProxy task : tasks) {
+ Log.i("Task output", task.getName() + "\n" + task.getNote());
+ }
+}
+</pre>
+
+<p>This {@link android.os.AsyncTask} returns a list of
+<code>TaskProxy</code> objects, and sends it to the debug {@code dump()} method
+upon completion. Note that if the argument list is empty, or the first argument
+is a -1, all tasks are retrieved from the server. Otherwise, only the ones with
+IDs in the supplied list are returned. All the fields you added to the task
+entity when building out the App Engine application are available via get/set
+methods in the <code>TaskProxy</code> class.</p>
+
+<p>In order to create new tasks and send them to the cloud, create a request
+object and use it to create a proxy object. Then populate the proxy object and
+call its update method. Once again, this should be done in an
+<code>AsyncTask</code> to avoid doing networking on the UI thread. The end
+result looks something like this.</p>
+
+<pre>
+new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... arg0) {
+ MyRequestFactory factory = (MyRequestFactory)
+ Util.getRequestFactory(TasksActivity.this,
+ MyRequestFactory.class);
+ TaskRequest request = factory.taskRequest();
+
+ // Create your local proxy object, populate it
+ TaskProxy task = request.create(TaskProxy.class);
+ task.setName(taskName);
+ task.setNote(taskDetails);
+ task.setDueDate(dueDate);
+
+ // To the cloud!
+ request.updateTask(task).fire();
+ return null;
+ }
+}.execute();
+</pre>
+
+<h2 id="serverc2dm">Configure the C2DM Server-Side</h2>
+
+<p>In order to set up C2DM messages to be sent to your Android device, go back
+into your App Engine codebase, and open up the service class that was created
+when you generated your RPC layer. If the name of your project is Foo,
+this class is called FooService. Add a line to each of the methods for
+adding, deleting, or updating data so that a C2DM message is sent to the
+user's device. Here's an example of an update task:
+</p>
+
+<pre>
+public static Task updateTask(Task task) {
+ task.setEmailAddress(DataStore.getUserEmail());
+ task = db.update(task);
+ DataStore.sendC2DMUpdate(TaskChange.UPDATE + TaskChange.SEPARATOR + task.getId());
+ return task;
+}
+
+// Helper method. Given a String, send it to the current user's device via C2DM.
+public static void sendC2DMUpdate(String message) {
+ UserService userService = UserServiceFactory.getUserService();
+ User user = userService.getCurrentUser();
+ ServletContext context = RequestFactoryServlet.getThreadLocalRequest().getSession().getServletContext();
+ SendMessage.sendMessage(context, user.getEmail(), message);
+}
+</pre>
+
+<p>In the following example, a helper class, {@code TaskChange}, has been created with a few
+constants. Creating such a helper class makes managing the communication
+between App Engine and Android apps much easier. Just create it in the shared
+folder, define a few constants (flags for what kind of message you're sending
+and a seperator is typically enough), and you're done. By way of example,
+the above code works off of a {@code TaskChange} class defined as this:</p>
+
+<pre>
+public class TaskChange {
+ public static String UPDATE = "Update";
+ public static String DELETE = "Delete";
+ public static String SEPARATOR = ":";
+}
+</pre>
+
+<h2 id="clientc2dm">Configure the C2DM Client-Side</h2>
+
+<p>In order to define the Android applications behavior when a C2DM is recieved,
+open up the <code>C2DMReceiver</code> class, and browse to the
+<code>onMessage()</code> method. Tweak this method to update based on the content
+of the message.</p>
+<pre>
+//In your C2DMReceiver class
+
+public void notifyListener(Intent intent) {
+ if (listener != null) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ String message = (String) extras.get("message");
+ String[] messages = message.split(Pattern.quote(TaskChange.SEPARATOR));
+ listener.onTaskUpdated(messages[0], Long.parseLong(messages[1]));
+ }
+ }
+}
+</pre>
+
+<pre>
+// Elsewhere in your code, wherever it makes sense to perform local updates
+public void onTasksUpdated(String messageType, Long id) {
+ if (messageType.equals(TaskChange.DELETE)) {
+ // Delete this task from your local data store
+ ...
+ } else {
+ // Call that monstrous Asynctask defined earlier.
+ fetchTasks(id);
+ }
+}
+</pre>
+<p>
+Once you have C2DM set up to trigger local updates, you're all done.
+Congratulations, you have a cloud-connected Android application!</p>
diff --git a/docs/html/training/cloudsync/backupapi.jd b/docs/html/training/cloudsync/backupapi.jd
new file mode 100644
index 0000000..3055596
--- /dev/null
+++ b/docs/html/training/cloudsync/backupapi.jd
@@ -0,0 +1,193 @@
+page.title=Using the Backup API
+parent.title=Syncing to the Cloud
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Syncing with App Engine
+previous.link=aesync.html
+
+@jd:body
+
+<div id="tb-wrapper">
+ <div id="tb">
+ <h2>This lesson teaches you to</h2>
+ <ol>
+ <li><a href="#register">Register for the Android Backup Service</a></li>
+ <li><a href="#manifest">Configure Your Manifest</a></li>
+ <li><a href="#agent">Write Your Backup Agent</a></li>
+ <li><a href="#backup">Request a Backup</a></li>
+ <li><a href="#restore">Restore from a Backup</a></li>
+ </ol>
+ <h2>You should also read</h2>
+ <ul>
+ <li><a
+ href="http://developer.android.com/guide/topics/data/backup.html">Data
+ Backup</a></li>
+ </ul>
+ </div>
+</div>
+
+<p>When a user purchases a new device or resets their existing one, they might
+expect that when Google Play restores your app back to their device during the
+initial setup, the previous data associated with the app restores as well. By
+default, that doesn't happen and all the user's accomplishments or settings in
+your app are lost.</p>
+<p>For situations where the volume of data is relatively light (less than a
+megabyte), like the user's preferences, notes, game high scores or other
+stats, the Backup API provides a lightweight solution. This lesson walks you
+through integrating the Backup API into your application, and restoring data to
+new devices using the Backup API.</p>
+
+<h2 id="register">Register for the Android Backup Service</h2>
+<p>This lesson requires the use of the <a
+ href="http://code.google.com/android/backup/index.html">Android Backup
+ Service</a>, which requires registration. Go ahead and <a
+ href="http://code.google.com/android/backup/signup.html">register here</a>. Once
+that's done, the service pre-populates an XML tag for insertion in your Android
+Manifest, which looks like this:</p>
+<pre>
+<meta-data android:name="com.google.android.backup.api_key"
+android:value="ABcDe1FGHij2KlmN3oPQRs4TUvW5xYZ" />
+</pre>
+<p>Note that each backup key works with a specific package name. If you have
+different applications, register separate keys for each one.</p>
+
+
+<h2 id="manifest">Configure Your Manifest</h2>
+<p>Use of the Android Backup Service requires two additions to your application
+manifest. First, declare the name of the class that acts as your backup agent,
+then add the snippet above as a child element of the Application tag. Assuming
+your backup agent is going to be called {@code TheBackupAgent}, here's an example of
+what the manifest looks like with this tag included:</p>
+
+<pre>
+<application android:label="MyApp"
+ android:backupAgent="TheBackupAgent">
+ ...
+ <meta-data android:name="com.google.android.backup.api_key"
+ android:value="ABcDe1FGHij2KlmN3oPQRs4TUvW5xYZ" />
+ ...
+</application>
+</pre>
+<h2 id="agent">Write Your Backup Agent</h2>
+<p>The easiest way to create your backup agent is by extending the wrapper class
+{@link android.app.backup.BackupAgentHelper}. Creating this helper class is
+actually a very simple process. Just create a class with the same name as you
+used in the manifest in the previous step (in this example, {@code
+TheBackupAgent}),
+and extend {@code BackupAgentHelper}. Then override the {@link
+android.app.backup.BackupAgent#onCreate()}.</p>
+
+<p>Inside the {@link android.app.backup.BackupAgent#onCreate()} method, create a {@link
+android.app.backup.BackupHelper}. These helpers are
+specialized classes for backing up certain kinds of data. The Android framework
+currently includes two such helpers: {@link
+android.app.backup.FileBackupHelper} and {@link
+android.app.backup.SharedPreferencesBackupHelper}. After you create the helper
+and point it at the data you want to back up, just add it to the
+BackupAgentHelper using the {@link android.app.backup.BackupAgentHelper#addHelper(String, BackupHelper) addHelper()}
+method, adding a key which is used to
+retrieve the data later. In most cases the entire
+implementation is perhaps 10 lines of code.</p>
+
+<p>Here's an example that backs up a high scores file.</p>
+
+<pre>
+ import android.app.backup.BackupAgentHelper;
+ import android.app.backup.FileBackupHelper;
+
+
+ public class TheBackupAgent extends BackupAgentHelper {
+ // The name of the SharedPreferences file
+ static final String HIGH_SCORES_FILENAME = "scores";
+
+ // A key to uniquely identify the set of backup data
+ static final String FILES_BACKUP_KEY = "myfiles";
+
+ // Allocate a helper and add it to the backup agent
+ @Override
+ void onCreate() {
+ FileBackupHelper helper = new FileBackupHelper(this, HIGH_SCORES_FILENAME);
+ addHelper(FILES_BACKUP_KEY, helper);
+ }
+}
+</pre>
+<p>For added flexibility, {@link android.app.backup.FileBackupHelper}'s
+constructor can take a variable number of filenames. You could just as easily
+have backed up both a high scores file and a game progress file just by adding
+an extra parameter, like this:</p>
+<pre>
+ @Override
+ void onCreate() {
+ FileBackupHelper helper = new FileBackupHelper(this, HIGH_SCORES_FILENAME, PROGRESS_FILENAME);
+ addHelper(FILES_BACKUP_KEY, helper);
+ }
+</pre>
+<p>Backing up preferences is similarly easy. Create a {@link
+android.app.backup.SharedPreferencesBackupHelper} the same way you did a {@link
+android.app.backup.FileBackupHelper}. In this case, instead of adding filenames
+to the constructor, add the names of the shared preference groups being used by
+your application. Here's an example of how your backup agent helper might look if
+high scores are implemented as preferences instead of a flat file:</p>
+
+<pre>
+ import android.app.backup.BackupAgentHelper;
+ import android.app.backup.SharedPreferencesBackupHelper;
+
+ public class TheBackupAgent extends BackupAgentHelper {
+ // The names of the SharedPreferences groups that the application maintains. These
+ // are the same strings that are passed to getSharedPreferences(String, int).
+ static final String PREFS_DISPLAY = "displayprefs";
+ static final String PREFS_SCORES = "highscores";
+
+ // An arbitrary string used within the BackupAgentHelper implementation to
+ // identify the SharedPreferencesBackupHelper's data.
+ static final String MY_PREFS_BACKUP_KEY = "myprefs";
+
+ // Simply allocate a helper and install it
+ void onCreate() {
+ SharedPreferencesBackupHelper helper =
+ new SharedPreferencesBackupHelper(this, PREFS_DISPLAY, PREFS_SCORES);
+ addHelper(MY_PREFS_BACKUP_KEY, helper);
+ }
+ }
+</pre>
+
+<p>You can add as many backup helper instances to your backup agent helper as you
+like, but remember that you only need one of each type. One {@link
+android.app.backup.FileBackupHelper} handles all the files that you need to back up, and one
+{@link android.app.backup.SharedPreferencesBackupHelper} handles all the shared
+preferencegroups you need backed up.
+</p>
+
+
+<h2 id="backup">Request a Backup</h2>
+<p>In order to request a backup, just create an instance of the {@link
+android.app.backup.BackupManager}, and call it's {@link
+android.app.backup.BackupManager#dataChanged()} method.</p>
+
+<pre>
+ import android.app.backup.BackupManager;
+ ...
+
+ public void requestBackup() {
+ BackupManager bm = new BackupManager(this);
+ bm.dataChanged();
+ }
+</pre>
+
+<p>This call notifies the backup manager that there is data ready to be backed
+up to the cloud. At some point in the future, the backup manager then calls
+your backup agent's {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput,
+ParcelFileDescriptor) onBackup()} method. You can make
+the call whenever your data has changed, without having to worry about causing
+excessive network activity. If you request a backup twice before a backup
+occurs, the backup only occurs once.</p>
+
+
+<h2 id="restore">Restore from a Backup</h2>
+<p>Typically you shouldn't ever have to manually request a restore, as it
+happens automatically when your application is installed on a device. However,
+if it <em>is</em> necessary to trigger a manual restore, just call the
+{@link android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()} method.</p>
diff --git a/docs/html/training/cloudsync/index.jd b/docs/html/training/cloudsync/index.jd
new file mode 100644
index 0000000..e53844b
--- /dev/null
+++ b/docs/html/training/cloudsync/index.jd
@@ -0,0 +1,34 @@
+page.title=Syncing to the Cloud
+
+trainingnavtop=true
+startpage=true
+next.title=Syncing with App Engine
+next.link=aesync.html
+
+@jd:body
+
+<p>By providing powerful APIs for internet connectivity, the Android framework
+helps you build rich cloud-enabled apps that sync their data to a remote web
+service, making sure all your devices always stay in sync, and your valuable
+data is always backed up to the cloud.</p>
+
+<p>This class covers different strategies for cloud enabled applications. It
+covers syncing data with the cloud using your own back-end web application, and
+backing up data using the cloud so that users can restore their data when
+installing your application on a new device.
+</p>
+
+<h2>Lessons</h2>
+
+<dl>
+ <dt><strong><a href="aesync.html">Syncing with App Engine.</a></strong></dt>
+ <dd>Learn how to create a paired App Engine app and Android app which share a
+ data model, authenticates using the AccountManager, and communicate with each
+ other via REST and C2DM.</dd>
+ <dt><strong><a href="backupapi.html">Using the Backup
+ API</a></strong></dt>
+ <dd>Learn how to integrate the Backup API into your Android Application, so
+ that user data such as preferences, notes, and high scores update seamlessly
+ across all of a user's devices</dd>
+</dl>
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index ede8e7a..dba70be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -354,9 +354,10 @@
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
+
// XXX: temporary: while testing big notifications, auto-expand all of them
ViewGroup.LayoutParams lp = row.getLayoutParams();
- if (sbn.notification.bigContentView != null) {
+ if (large != null) {
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
} else {
lp.height = minHeight;
@@ -372,9 +373,16 @@
// bind the click event to the content area
ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
ViewGroup adaptive = (ViewGroup)row.findViewById(R.id.adaptive);
- // XXX: update to allow controls within notification views
+
+ // Ensure that R.id.content is properly set to 64dp high if 1U
+ lp = content.getLayoutParams();
+ if (large == null) {
+ lp.height = minHeight;
+ }
+ content.setLayoutParams(lp);
+
content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-// content.setOnFocusChangeListener(mFocusChangeListener);
+
PendingIntent contentIntent = sbn.notification.contentIntent;
if (contentIntent != null) {
final View.OnClickListener listener = new NotificationClicker(contentIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f45b3ad..76f7ea6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -94,7 +94,7 @@
public static final String ACTION_STATUSBAR_START
= "com.android.internal.policy.statusbar.START";
- private static final boolean ENABLE_INTRUDERS = true;
+ private static final boolean ENABLE_INTRUDERS = false;
static final int EXPANDED_LEAVE_ALONE = -10000;
static final int EXPANDED_FULL_OPEN = -10001;
@@ -280,9 +280,11 @@
}
mNotificationPanel = expanded.findViewById(R.id.notification_panel);
- mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null);
- mIntruderAlertView.setVisibility(View.GONE);
- mIntruderAlertView.setBar(this);
+ if (ENABLE_INTRUDERS) {
+ mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null);
+ mIntruderAlertView.setVisibility(View.GONE);
+ mIntruderAlertView.setBar(this);
+ }
PhoneStatusBarView sb = (PhoneStatusBarView)View.inflate(context,
R.layout.status_bar, null);
@@ -520,12 +522,12 @@
}
} catch (RemoteException ex) {
}
+
+ /*
+ * DISABLED due to missing API
if (ENABLE_INTRUDERS && (
// TODO(dsandler): Only if the screen is on
notification.notification.intruderView != null)) {
-// notification.notification.fullScreenIntent != null
-// || (notification.score >= mIntruderInImmersiveMinScore)
-// || (!immersive && (notification.score > mIntruderMinScore)))) {
Slog.d(TAG, "Presenting high-priority notification");
// special new transient ticker mode
// 1. Populate mIntruderAlertView
@@ -554,7 +556,10 @@
if (INTRUDER_ALERT_DECAY_MS > 0) {
mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
}
- } else if (notification.notification.fullScreenIntent != null) {
+ } else
+ */
+
+ if (notification.notification.fullScreenIntent != null) {
// not immersive & a full-screen alert should be shown
Slog.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
try {
@@ -675,7 +680,7 @@
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
// See if we need to update the intruder.
- if (oldNotification == mCurrentlyIntrudingNotification) {
+ if (ENABLE_INTRUDERS && oldNotification == mCurrentlyIntrudingNotification) {
if (DEBUG) Slog.d(TAG, "updating the current intruder:" + notification);
// XXX: this is a hack for Alarms. The real implementation will need to *update*
// the intruder.
@@ -697,7 +702,7 @@
// Recalculate the position of the sliding windows and the titles.
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
- if (old == mCurrentlyIntrudingNotification) {
+ if (ENABLE_INTRUDERS && old == mCurrentlyIntrudingNotification) {
mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
}
@@ -2039,6 +2044,7 @@
};
private void setIntruderAlertVisibility(boolean vis) {
+ if (!ENABLE_INTRUDERS) return;
if (DEBUG) {
Slog.v(TAG, (vis ? "showing" : "hiding") + " intruder alert window");
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
old mode 100644
new mode 100755
index 7aee8d2..b4a458f
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -3557,17 +3557,7 @@
continue;
}
- if (!haveGroup) {
- // We ignore any hidden applications on the top.
- if (wtoken.hiddenRequested || wtoken.willBeHidden) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
- + " -- hidden on top");
- continue;
- }
- haveGroup = true;
- curGroup = wtoken.groupId;
- lastOrientation = wtoken.requestedOrientation;
- } else if (curGroup != wtoken.groupId) {
+ if (haveGroup == true && curGroup != wtoken.groupId) {
// If we have hit a new application group, and the bottom
// of the previous group didn't explicitly say to use
// the orientation behind it, and the last app was
@@ -3580,6 +3570,20 @@
return lastOrientation;
}
}
+
+ // We ignore any hidden applications on the top.
+ if (wtoken.hiddenRequested || wtoken.willBeHidden) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
+ + " -- hidden on top");
+ continue;
+ }
+
+ if (!haveGroup) {
+ haveGroup = true;
+ curGroup = wtoken.groupId;
+ lastOrientation = wtoken.requestedOrientation;
+ }
+
int or = wtoken.requestedOrientation;
// If this application is fullscreen, and didn't explicitly say
// to use the orientation behind it, then just take whatever
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 5eac1f2..a90af15 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
import android.os.Environment;
import android.os.Vibrator;
import android.os.Handler;
@@ -44,10 +45,10 @@
private final static String TAG = "NotificationTestList";
NotificationManager mNM;
- Vibrator mVibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE);
+ Vibrator mVibrator;
Handler mHandler = new Handler();
- long mActivityCreateTime = System.currentTimeMillis();
+ long mActivityCreateTime;
long mChronometerBase = 0;
boolean mProgressDone = true;
@@ -67,6 +68,13 @@
final int kUnnumberedIconResID = R.drawable.notificationx;
@Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ mVibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE);
+ mActivityCreateTime = System.currentTimeMillis();
+ }
+
+ @Override
protected String tag() {
return TAG;
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 314e33e..6168f0e 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -1500,8 +1500,7 @@
int key;
WifiP2pServiceRequest req;
for (int i=0; i < c.mReqList.size(); i++) {
- key = c.mReqList.keyAt(i);
- req = c.mReqList.get(key);
+ req = c.mReqList.valueAt(i);
if (req != null) {
sb.append(req.getSupplicantQuery());
}
@@ -1539,7 +1538,10 @@
return false;
}
- req.setTransactionId(++mServiceTransactionId);
+ ++mServiceTransactionId;
+ //The Wi-Fi p2p spec says transaction id should be non-zero
+ if (mServiceTransactionId == 0) ++mServiceTransactionId;
+ req.setTransactionId(mServiceTransactionId);
clientInfo.mReqList.put(mServiceTransactionId, req);
if (mServiceDiscReqId == null) {
@@ -1550,13 +1552,23 @@
}
private void removeServiceRequest(Messenger m, WifiP2pServiceRequest req) {
-
ClientInfo clientInfo = getClientInfo(m, false);
if (clientInfo == null) {
return;
}
- clientInfo.mReqList.remove(req.getTransactionId());
+ //Application does not have transaction id information
+ //go through stored requests to remove
+ boolean removed = false;
+ for (int i=0; i < clientInfo.mReqList.size(); i++) {
+ if (req.equals(clientInfo.mReqList.valueAt(i))) {
+ removed = true;
+ clientInfo.mReqList.removeAt(i);
+ break;
+ }
+ }
+
+ if (!removed) return;
if (clientInfo.mReqList.size() == 0 && clientInfo.mServList.size() == 0) {
if (DBG) logd("remove client information from framework");
@@ -1670,6 +1682,7 @@
} catch (RemoteException e) {
if (DBG) logd("detect dead channel");
clearClientInfo(c.mMessenger);
+ return;
}
}
}
@@ -1683,6 +1696,8 @@
* TODO: This can be done better with full async channels.
*/
private void clearClientDeadChannels() {
+ ArrayList<Messenger> deadClients = new ArrayList<Messenger>();
+
for (ClientInfo c : mClientInfoList.values()) {
Message msg = Message.obtain();
msg.what = WifiP2pManager.PING;
@@ -1693,9 +1708,13 @@
c.mMessenger.send(msg);
} catch (RemoteException e) {
if (DBG) logd("detect dead channel");
- clearClientInfo(c.mMessenger);
+ deadClients.add(c.mMessenger);
}
}
+
+ for (Messenger m : deadClients) {
+ clearClientInfo(m);
+ }
}
/**