Merge "Check for ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION" into mnc-dev
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b0d8541..fc71783 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1862,6 +1862,9 @@
} else {
sb.append("null");
}
+ if (this.tickerText != null) {
+ sb.append(" tick");
+ }
sb.append(" defaults=0x");
sb.append(Integer.toHexString(this.defaults));
sb.append(" flags=0x");
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 3cc7684..ba9cf7c 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -16,8 +16,11 @@
package android.content;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -40,8 +43,8 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.UserHandle;
-import android.util.Log;
import android.text.TextUtils;
+import android.util.Log;
import java.io.File;
import java.io.FileDescriptor;
@@ -474,14 +477,9 @@
private int enforceReadPermission(String callingPkg, Uri uri, IBinder callerToken)
throws SecurityException {
- enforceReadPermissionInner(uri, callerToken);
-
- final int permOp = AppOpsManager.permissionToOpCode(mReadPermission);
- if (permOp != AppOpsManager.OP_NONE) {
- final int mode = mAppOpsManager.noteProxyOp(permOp, callingPkg);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- return mode;
- }
+ final int mode = enforceReadPermissionInner(uri, callingPkg, callerToken);
+ if (mode != MODE_ALLOWED) {
+ return mode;
}
if (mReadOp != AppOpsManager.OP_NONE) {
@@ -493,14 +491,9 @@
private int enforceWritePermission(String callingPkg, Uri uri, IBinder callerToken)
throws SecurityException {
- enforceWritePermissionInner(uri, callerToken);
-
- final int permOp = AppOpsManager.permissionToOpCode(mWritePermission);
- if (permOp != AppOpsManager.OP_NONE) {
- final int mode = mAppOpsManager.noteProxyOp(permOp, callingPkg);
- if (mode != AppOpsManager.MODE_ALLOWED) {
- return mode;
- }
+ final int mode = enforceWritePermissionInner(uri, callingPkg, callerToken);
+ if (mode != MODE_ALLOWED) {
+ return mode;
}
if (mWriteOp != AppOpsManager.OP_NONE) {
@@ -518,26 +511,47 @@
== PERMISSION_GRANTED;
}
+ /**
+ * Verify that calling app holds both the given permission and any app-op
+ * associated with that permission.
+ */
+ private int checkPermissionAndAppOp(String permission, String callingPkg,
+ IBinder callerToken) {
+ if (getContext().checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid(),
+ callerToken) != PERMISSION_GRANTED) {
+ return MODE_ERRORED;
+ }
+
+ final int permOp = AppOpsManager.permissionToOpCode(permission);
+ if (permOp != AppOpsManager.OP_NONE) {
+ return mTransport.mAppOpsManager.noteProxyOp(permOp, callingPkg);
+ }
+
+ return MODE_ALLOWED;
+ }
+
/** {@hide} */
- protected void enforceReadPermissionInner(Uri uri, IBinder callerToken)
+ protected int enforceReadPermissionInner(Uri uri, String callingPkg, IBinder callerToken)
throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
String missingPerm = null;
+ int strongestMode = MODE_ALLOWED;
if (UserHandle.isSameApp(uid, mMyUid)) {
- return;
+ return MODE_ALLOWED;
}
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getReadPermission();
if (componentPerm != null) {
- if (context.checkPermission(componentPerm, pid, uid, callerToken)
- == PERMISSION_GRANTED) {
- return;
+ final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken);
+ if (mode == MODE_ALLOWED) {
+ return MODE_ALLOWED;
} else {
missingPerm = componentPerm;
+ strongestMode = Math.max(strongestMode, mode);
}
}
@@ -551,14 +565,15 @@
for (PathPermission pp : pps) {
final String pathPerm = pp.getReadPermission();
if (pathPerm != null && pp.match(path)) {
- if (context.checkPermission(pathPerm, pid, uid, callerToken)
- == PERMISSION_GRANTED) {
- return;
+ final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken);
+ if (mode == MODE_ALLOWED) {
+ return MODE_ALLOWED;
} else {
// any denied <path-permission> means we lose
// default <provider> access.
allowDefaultRead = false;
missingPerm = pathPerm;
+ strongestMode = Math.max(strongestMode, mode);
}
}
}
@@ -566,7 +581,7 @@
// if we passed <path-permission> checks above, and no default
// <provider> permission, then allow access.
- if (allowDefaultRead) return;
+ if (allowDefaultRead) return MODE_ALLOWED;
}
// last chance, check against any uri grants
@@ -575,7 +590,13 @@
? maybeAddUserId(uri, callingUserId) : uri;
if (context.checkUriPermission(userUri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION,
callerToken) == PERMISSION_GRANTED) {
- return;
+ return MODE_ALLOWED;
+ }
+
+ // If the worst denial we found above was ignored, then pass that
+ // ignored through; otherwise we assume it should be a real error below.
+ if (strongestMode == MODE_IGNORED) {
+ return MODE_IGNORED;
}
final String failReason = mExported
@@ -587,25 +608,27 @@
}
/** {@hide} */
- protected void enforceWritePermissionInner(Uri uri, IBinder callerToken)
+ protected int enforceWritePermissionInner(Uri uri, String callingPkg, IBinder callerToken)
throws SecurityException {
final Context context = getContext();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
String missingPerm = null;
+ int strongestMode = MODE_ALLOWED;
if (UserHandle.isSameApp(uid, mMyUid)) {
- return;
+ return MODE_ALLOWED;
}
if (mExported && checkUser(pid, uid, context)) {
final String componentPerm = getWritePermission();
if (componentPerm != null) {
- if (context.checkPermission(componentPerm, pid, uid, callerToken)
- == PERMISSION_GRANTED) {
- return;
+ final int mode = checkPermissionAndAppOp(componentPerm, callingPkg, callerToken);
+ if (mode == MODE_ALLOWED) {
+ return MODE_ALLOWED;
} else {
missingPerm = componentPerm;
+ strongestMode = Math.max(strongestMode, mode);
}
}
@@ -619,14 +642,15 @@
for (PathPermission pp : pps) {
final String pathPerm = pp.getWritePermission();
if (pathPerm != null && pp.match(path)) {
- if (context.checkPermission(pathPerm, pid, uid, callerToken)
- == PERMISSION_GRANTED) {
- return;
+ final int mode = checkPermissionAndAppOp(pathPerm, callingPkg, callerToken);
+ if (mode == MODE_ALLOWED) {
+ return MODE_ALLOWED;
} else {
// any denied <path-permission> means we lose
// default <provider> access.
allowDefaultWrite = false;
missingPerm = pathPerm;
+ strongestMode = Math.max(strongestMode, mode);
}
}
}
@@ -634,13 +658,19 @@
// if we passed <path-permission> checks above, and no default
// <provider> permission, then allow access.
- if (allowDefaultWrite) return;
+ if (allowDefaultWrite) return MODE_ALLOWED;
}
// last chance, check against any uri grants
if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
callerToken) == PERMISSION_GRANTED) {
- return;
+ return MODE_ALLOWED;
+ }
+
+ // If the worst denial we found above was ignored, then pass that
+ // ignored through; otherwise we assume it should be a real error below.
+ if (strongestMode == MODE_IGNORED) {
+ return MODE_IGNORED;
}
final String failReason = mExported
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 6979bee..5a341fc 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -640,7 +640,7 @@
final Bundle out = new Bundle();
try {
if (METHOD_CREATE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), null);
final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
@@ -654,7 +654,7 @@
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
} else if (METHOD_RENAME_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), null);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
final String newDocumentId = renameDocument(documentId, displayName);
@@ -678,7 +678,7 @@
}
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
- enforceWritePermissionInner(documentUri, null);
+ enforceWritePermissionInner(documentUri, getCallingPackage(), null);
deleteDocument(documentId);
// Document no longer exists, clean up any grants
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 0b18bb8..4737e9b 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -284,19 +284,13 @@
* currently set for that origin. The host application should invoke the
* specified callback with the desired permission state. See
* {@link GeolocationPermissions} for details.
- *
- * If this method isn't overridden, the callback is invoked with permission
- * denied state.
- *
* @param origin The origin of the web content attempting to use the
* Geolocation API.
* @param callback The callback to use to set the permission state for the
* origin.
*/
public void onGeolocationPermissionsShowPrompt(String origin,
- GeolocationPermissions.Callback callback) {
- callback.invoke(origin, false, false);
- }
+ GeolocationPermissions.Callback callback) {}
/**
* Notify the host application that a request for Geolocation permissions,
diff --git a/core/java/com/android/internal/midi/MidiConstants.java b/core/java/com/android/internal/midi/MidiConstants.java
index c13e5fc..b6b8bf0 100644
--- a/core/java/com/android/internal/midi/MidiConstants.java
+++ b/core/java/com/android/internal/midi/MidiConstants.java
@@ -55,18 +55,30 @@
public final static int SYSTEM_BYTE_LENGTHS[] = { 1, 2, 3, 2, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1 };
- /********************************************************************/
- public static int getBytesPerMessage(int command) {
- if ((command < 0x80) || (command > 0xFF)) {
- return 0;
- } else if (command >= 0xF0) {
- return SYSTEM_BYTE_LENGTHS[command & 0x0F];
+ /**
+ * MIDI messages, except for SysEx, are 1,2 or 3 bytes long.
+ * You can tell how long a MIDI message is from the first status byte.
+ * Do not call this for SysEx, which has variable length.
+ * @param statusByte
+ * @return number of bytes in a complete message or zero if data byte passed
+ */
+ public static int getBytesPerMessage(byte statusByte) {
+ // Java bytes are signed so we need to mask off the high bits
+ // to get a value between 0 and 255.
+ int statusInt = statusByte & 0xFF;
+ if (statusInt >= 0xF0) {
+ // System messages use low nibble for size.
+ return SYSTEM_BYTE_LENGTHS[statusInt & 0x0F];
+ } else if(statusInt >= 0x80) {
+ // Channel voice messages use high nibble for size.
+ return CHANNEL_BYTE_LENGTHS[(statusInt >> 4) - 8];
} else {
- return CHANNEL_BYTE_LENGTHS[(command >> 4) - 8];
+ return 0; // data byte
}
}
+
/**
* @param msg
* @param offset
diff --git a/core/java/com/android/internal/midi/MidiFramer.java b/core/java/com/android/internal/midi/MidiFramer.java
index 058f57c..62517fa 100644
--- a/core/java/com/android/internal/midi/MidiFramer.java
+++ b/core/java/com/android/internal/midi/MidiFramer.java
@@ -17,7 +17,7 @@
package com.android.internal.midi;
import android.media.midi.MidiReceiver;
-import android.util.Log;
+//import android.util.Log;
import java.io.IOException;
@@ -37,7 +37,7 @@
private MidiReceiver mReceiver;
private byte[] mBuffer = new byte[3];
private int mCount;
- private int mRunningStatus;
+ private byte mRunningStatus;
private int mNeeded;
private boolean mInSysEx;
@@ -59,22 +59,22 @@
@Override
public void onSend(byte[] data, int offset, int count, long timestamp)
throws IOException {
- // Log.i(TAG, formatMidiData(data, offset, count));
int sysExStartOffset = (mInSysEx ? offset : -1);
for (int i = 0; i < count; i++) {
- int b = data[offset] & 0xFF;
- if (b >= 0x80) { // status byte?
- if (b < 0xF0) { // channel message?
- mRunningStatus = (byte) b;
+ final byte currentByte = data[offset];
+ final int currentInt = currentByte & 0xFF;
+ if (currentInt >= 0x80) { // status byte?
+ if (currentInt < 0xF0) { // channel message?
+ mRunningStatus = currentByte;
mCount = 1;
- mNeeded = MidiConstants.getBytesPerMessage(b) - 1;
- } else if (b < 0xF8) { // system common?
- if (b == 0xF0 /* SysEx Start */) {
+ mNeeded = MidiConstants.getBytesPerMessage(currentByte) - 1;
+ } else if (currentInt < 0xF8) { // system common?
+ if (currentInt == 0xF0 /* SysEx Start */) {
// Log.i(TAG, "SysEx Start");
mInSysEx = true;
sysExStartOffset = offset;
- } else if (b == 0xF7 /* SysEx End */) {
+ } else if (currentInt == 0xF7 /* SysEx End */) {
// Log.i(TAG, "SysEx End");
if (mInSysEx) {
mReceiver.send(data, sysExStartOffset,
@@ -83,10 +83,10 @@
sysExStartOffset = -1;
}
} else {
- mBuffer[0] = (byte) b;
+ mBuffer[0] = currentByte;
mRunningStatus = 0;
mCount = 1;
- mNeeded = MidiConstants.getBytesPerMessage(b) - 1;
+ mNeeded = MidiConstants.getBytesPerMessage(currentByte) - 1;
}
} else { // real-time?
// Single byte message interleaved with other data.
@@ -98,12 +98,11 @@
mReceiver.send(data, offset, 1, timestamp);
}
} else { // data byte
- // Save SysEx data for SysEx End marker or end of buffer.
if (!mInSysEx) {
- mBuffer[mCount++] = (byte) b;
+ mBuffer[mCount++] = currentByte;
if (--mNeeded == 0) {
if (mRunningStatus != 0) {
- mBuffer[0] = (byte) mRunningStatus;
+ mBuffer[0] = mRunningStatus;
}
mReceiver.send(mBuffer, 0, mCount, timestamp);
mNeeded = MidiConstants.getBytesPerMessage(mBuffer[0]) - 1;
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 0d80a7f..670d3c01 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -475,6 +475,14 @@
}
}
+static void FromColor_DA8(void* dst, const SkColor src[], int width, int x, int y) {
+ uint8_t* d = (uint8_t*)dst;
+
+ for (int stop = x + width; x < stop; x++) {
+ *d++ = SkColorGetA(*src++);
+ }
+}
+
// can return NULL
static FromColorProc ChooseFromColorProc(const SkBitmap& bitmap) {
switch (bitmap.colorType()) {
@@ -485,6 +493,8 @@
FromColor_D4444_Raw;
case kRGB_565_SkColorType:
return FromColor_D565;
+ case kAlpha_8_SkColorType:
+ return FromColor_DA8;
default:
break;
}
@@ -632,6 +642,15 @@
} while (--width != 0);
}
+static void ToColor_SA8(SkColor dst[], const void* src, int width, SkColorTable*) {
+ SkASSERT(width > 0);
+ const uint8_t* s = (const uint8_t*)src;
+ do {
+ uint8_t c = *s++;
+ *dst++ = SkColorSetARGB(c, c, c, c);
+ } while (--width != 0);
+}
+
// can return NULL
static ToColorProc ChooseToColorProc(const SkBitmap& src) {
switch (src.colorType()) {
@@ -673,6 +692,8 @@
default:
return NULL;
}
+ case kAlpha_8_SkColorType:
+ return ToColor_SA8;
default:
break;
}
diff --git a/docs/html/tools/data-binding/guide.jd b/docs/html/tools/data-binding/guide.jd
index 7c4c15a..2de5bc2 100644
--- a/docs/html/tools/data-binding/guide.jd
+++ b/docs/html/tools/data-binding/guide.jd
@@ -28,6 +28,10 @@
<li>
<a href="#binding_data">Binding Data</a>
</li>
+
+ <li>
+ <a href="#binding_events">Binding Events</a>
+ </li>
</ol>
</li>
@@ -141,7 +145,7 @@
— it's a support library, so you can use it with all Android platform
versions back to <strong>Android 2.1</strong> (API level 7+).</p>
-<p>To use data binding, Android Plugin for Gradle <strong>1.3.0-beta1</strong>
+<p>To use data binding, Android Plugin for Gradle <strong>1.3.0-beta4</strong>
or higher is required.</p>
<h4>Beta release</h4>
@@ -187,9 +191,9 @@
<p>To get started with Data Binding, download the library from the Support
repository in the Android SDK manager. </p>
-<p>The Data Binding plugin requires Android Plugin for Gradle <strong>1.3.0-beta1
-or higher</strong>, so update your build dependencies (in
-<code>build.gradle</code>) as needed.</p>
+<p>The Data Binding plugin requires Android Plugin for Gradle <strong>1.3.0-beta4
+or higher</strong>, so update your build dependencies (in the top-level
+<code>build.gradle</code> file) as needed.</p>
<p>Also, make sure you are using a compatible version of Android Studio.
<strong>Android Studio 1.3</strong> adds the code-completion and layout-preview
@@ -201,18 +205,18 @@
<p>
To set up your application to use data binding, add data binding to the class
- path of your <code>build.gradle</code> file, right below "android".
+ path of your top-level <code>build.gradle</code> file, right below "android".
</p>
<pre>
dependencies {
- classpath <strong>"com.android.tools.build:gradle:1.3.0-beta1"
- </strong>classpath <strong>"com.android.databinding:dataBinder:</strong>1.0-rc0"
+ classpath <strong>"com.android.tools.build:gradle:1.3.0-beta4"</strong>
+ classpath <strong>"com.android.databinding:dataBinder:1.0-rc1"</strong>
}
-}
</pre>
<p>
- Then make sure jcenter is in the repositories list for your sub projects.
+ Then make sure jcenter is in the repositories list for your projects in the top-level
+ <code>build.gradle</code> file.
</p>
<pre>
@@ -228,8 +232,8 @@
</p>
<pre>
-apply plugin: ‘com.android.application'
-apply plugin: '<strong>com.android.databinding</strong>'
+apply plugin: 'com.android.application'
+apply plugin: 'com.android.databinding'
</pre>
<p>
The data binding plugin is going to add necessary <strong>provided</strong>
@@ -252,23 +256,23 @@
</p>
<pre>
-<em><?</em><strong>xml version="1.0" encoding="utf-8"</strong><em>?>
-</em><<strong>layout xmlns:android="http://schemas.android.com/apk/res/android"</strong>>
- <<strong>data</strong>>
- <<strong>variable name="user" type="com.example.User"</strong>/>
- </<strong>data</strong>>
- <<strong>LinearLayout
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <data>
+ <variable name="user" type="com.example.User"/>
+ </data>
+ <LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent"</strong>>
- <<strong>TextView android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+ <TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@{user.firstName}"</strong>/>
- <<strong>TextView android:layout_width="wrap_content"
+ android:text="@{user.firstName}"/>
+ <TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@{user.lastName}"</strong>/>
- </<strong>LinearLayout</strong>>
-</<strong>layout</strong>>
+ android:text="@{user.lastName}"/>
+ </LinearLayout>
+</layout>
</pre>
<p>
The user <strong>variable</strong> within <strong>data</strong> describes a
@@ -285,9 +289,9 @@
</p>
<pre>
-<<strong>TextView android:layout_width="wrap_content"
+<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@{user.firstName}"</strong>/>
+ android:text="@{user.firstName}"/>
</pre>
<h3 id="data_object">
Data Object
@@ -298,12 +302,12 @@
</p>
<pre>
-<strong>public class </strong>User {
- <strong>public final </strong>String <strong>firstName</strong>;
- <strong>public final </strong>String <strong>lastName</strong>;
- <strong>public </strong>User(String firstName, String lastName) {
- <strong>this</strong>.<strong>firstName </strong>= firstName;
- <strong>this</strong>.<strong>lastName </strong>= lastName;
+public class User {
+ public final String firstName;
+ public final String lastName;
+ public User(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
}
}
</pre>
@@ -314,18 +318,18 @@
</p>
<pre>
-<strong>public class </strong>User {
- <strong>private final </strong>String <strong>firstName</strong>;
- <strong>private final </strong>String <strong>lastName</strong>;
- <strong>public </strong>User(String firstName, String lastName) {
- <strong>this</strong>.<strong>firstName </strong>= firstName;
- <strong>this</strong>.<strong>lastName </strong>= lastName;
+public class User {
+ private final String firstName;
+ private final String lastName;
+ public User(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
}
- <strong>public </strong>String getFirstName() {
- <strong>return this</strong>.<strong>firstName</strong>;
+ public String getFirstName() {
+ return this.firstName;
}
- <strong>public </strong>String getLastName() {
- <strong>return this</strong>.<strong>lastName</strong>;
+ public String getLastName() {
+ return this.lastName;
}
}
</pre>
@@ -334,7 +338,8 @@
expression <strong><code>@{user.firstName}</code></strong> used for
the TextView’s <strong><code>android:text</code></strong> attribute will
access the <strong><code>firstName</code></strong> field in the former class
- and the <code>getFirstName()</code> method in the latter class.
+ and the <code>getFirstName()</code> method in the latter class. Alternatively, it
+ will also be resolved to <code>firstName()</code> if that method exists.
</p>
<h3 id="binding_data">
@@ -344,8 +349,8 @@
<p>
By default, a Binding class will be generated based on the name of the layout
file, converting it to Pascal case and suffixing “Binding” to it. The above
- layout file was <code>activity_main.xml</code> so the generate class was
- <code>ActivityMainBinding</code>. This class holds all the bindings from the
+ layout file was <code>main_activity.xml</code> so the generate class was
+ <code>MainActivityBinding</code>. This class holds all the bindings from the
layout properties (e.g. the <code>user</code> variable) to the layout’s Views
and knows how to assign values for the binding expressions.The easiest means
for creating the bindings is to do it while inflating:
@@ -353,10 +358,10 @@
<pre>
@Override
-<strong>protected void </strong>onCreate(Bundle savedInstanceState) {
- <strong>super</strong>.onCreate(savedInstanceState);
- ActivityMainBinding binding = DataBindingUtil.<em>setContentView</em>(<strong>this</strong>, R.layout.<em><strong>main_activity</strong></em>);
- User user = <strong>new </strong>User(<strong>"Test"</strong>, <strong>"User"</strong>);
+protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
+ User user = new User("Test", "User");
binding.setUser(user);
}
</pre>
@@ -374,11 +379,55 @@
</p>
<pre>
-ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup,
-false);
+ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.<em>inflate</em>(layoutInflater, R.layout.<em><strong>list_item</strong></em>, viewGroup, <strong>false</strong>);
</pre>
+
+<h3 id="binding_events">
+ Binding Events
+</h3>
+<p>
+ Events may be bound to handler methods directly, similar to the way
+ <strong><code>android:onClick</code></strong> can be assigned to a method in the Activity.
+ Event attribute names are governed by the name of the listener method with a few exceptions.
+ For example, {@link android.view.View.OnLongClickListener} has a method {@link android.view.View.OnLongClickListener#onLongClick onLongClick()},
+ so the attribute for this event is <code>android:onLongClick</code>.
+</p>
+<p>
+ To assign an event to its handler, use a normal binding expression, with the value
+ being the method name to call. For example, if your data object has two methods:
+</p>
+<pre>public class MyHandlers {
+ public void onClickFriend(View view) { ... }
+ public void onClickEnemy(View view) { ... }
+}
+</pre>
+<p>
+ The binding expression can assign the click listener for a View:
+</p>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <data>
+ <variable name="handlers" type="com.example.Handlers"/>
+ <variable name="user" type="com.example.User"/>
+ </data>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@{user.firstName}"
+ android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/>
+ <TextView android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@{user.lastName}"
+ android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/>
+ </LinearLayout>
+</layout>
+</pre>
<h2 id="layout_details">
Layout Details
</h2>
@@ -394,20 +443,20 @@
</p>
<pre>
-<<strong>data</strong>>
- <<strong>import type="android.view.View"</strong>/>
-</<strong>data</strong>>
+<data>
+ <import type="android.view.View"/>
+</data>
</pre>
<p>
Now, View may be used within your binding expression:
</p>
<pre>
-<<strong>TextView
+<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"</strong>/>
+ android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
</pre>
<p>
When there are class name conflicts, one of the classes may be renamed to an
@@ -415,9 +464,9 @@
</p>
<pre>
-<<strong>import type="android.view.View"</strong>/>
-<<strong>import type="com.example.real.estate.View"
- alias="Vista"</strong>/>
+<import type="android.view.View"/>
+<import type="com.example.real.estate.View"
+ alias="Vista"/>
</pre>
<p>
Now, <strong><code>Vista</code></strong> may be used to reference the
@@ -428,12 +477,12 @@
</p>
<pre>
-<<strong>data</strong>>
- <<strong>import type="com.example.User"</strong>/>
- <<strong>import type="java.util.List"</strong>/>
- <<strong>variable name="user" type="User"</strong>/>
- <<strong>variable name="userList" type="List&lt;User>"</strong>/>
- </<strong>data</strong>>
+<data>
+ <import type="com.example.User"/>
+ <import type="java.util.List"/>
+ <variable name="user" type="User"/>
+ <variable name="userList" type="List&lt;User>"/>
+</data>
</pre>
<p class="caution">
<strong>Note</strong>: Android Studio does not yet handle imports so the
@@ -443,10 +492,10 @@
</p>
<pre>
-<<strong>TextView
+<TextView
android:text="@{((User)(user.connection)).lastName}"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/>
+ android:layout_height="wrap_content"/>
</pre>
<p>
Imported types may also be used when referencing static fields and methods in
@@ -454,15 +503,15 @@
</p>
<pre>
-<<strong>data</strong>>
- <<strong>import type="com.example.MyStringUtils"</strong>/>
- <<strong>variable name="user" type="com.example.User"</strong>/>
-</<strong>data</strong>>
+<data>
+ <import type="com.example.MyStringUtils"/>
+ <variable name="user" type="com.example.User"/>
+</data>
…
-<<strong>TextView
+<TextView
android:text="@{MyStringUtils.capitalize(user.lastName)}"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/>
+ android:layout_height="wrap_content"/>
</pre>
<p>
Just as in Java, <code>java.lang.*</code> is imported automatically.
@@ -481,16 +530,16 @@
</p>
<pre>
-<<strong>data</strong>>
- <<strong>import type="android.graphics.drawable.Drawable"</strong>/>
- <<strong>variable name="user" type="com.example.User"</strong>/>
- <<strong>variable name="image" type="Drawable"</strong>/>
- <<strong>variable name="note" type="String"</strong>/>
-</<strong>data</strong>>
+<data>
+ <import type="android.graphics.drawable.Drawable"/>
+ <variable name="user" type="com.example.User"/>
+ <variable name="image" type="Drawable"/>
+ <variable name="note" type="String"/>
+</data>
</pre>
<p>
The variable types are inspected at compile time, so if a variable implements
- <a href="#observable_objects">Observable</a> or is an <a href=
+ {@link android.databinding.Observable} or is an <a href=
"#observable_collections">observable collection</a>, that should be reflected
in the type. If the variable is a base class or interface that does not
implement the Observable* interface, the variables will <strong>not
@@ -533,9 +582,9 @@
</p>
<pre>
-<<strong>data class="ContactItem"</strong>>
+<data class="ContactItem">
...
-</<strong>data</strong>>
+</data>
</pre>
<p>
This generates the binding class as <code>ContactItem</code> in the
@@ -545,9 +594,9 @@
</p>
<pre>
-<<strong>data class=".ContactItem"</strong>>
+<data class=".ContactItem">
...
-</<strong>data</strong>>
+</data>
</pre>
<p>
In this case, <code>ContactItem</code> is generated in the module package
@@ -555,9 +604,9 @@
</p>
<pre>
-<<strong>data class="com.example.ContactItem"</strong>>
+<data class="com.example.ContactItem">
...
-</<strong>data</strong>>
+</data>
</pre>
<h3 id="includes">
Includes
@@ -570,28 +619,46 @@
</p>
<pre>
-<em><?</em><strong>xml version="1.0" encoding="utf-8"</strong><em>?>
-</em><<strong>layout xmlns:android="http://schemas.android.com/apk/res/android"
-</strong> <strong> xmlns:bind="http://schemas.android.com/apk/res-auto"</strong>>
- <<strong>data</strong>>
- <<strong>variable name="user" type="com.example.User"</strong>/>
- </<strong>data</strong>>
- <<strong>LinearLayout
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bind="http://schemas.android.com/apk/res-auto">
+ <data>
+ <variable name="user" type="com.example.User"/>
+ </data>
+ <LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent"</strong>>
- <<strong>include layout="@layout/name"
- bind:user="@{user}"</strong>/>
- <<strong>include layout="@layout/contact"
- bind:user="@{user}"</strong>/>
- </<strong>LinearLayout</strong>>
-</<strong>layout</strong>>
+ android:layout_height="match_parent">
+ <include layout="@layout/name"
+ bind:user="@{user}"/>
+ <include layout="@layout/contact"
+ bind:user="@{user}"/>
+ </LinearLayout>
+</layout>
</pre>
<p>
Here, there must be a <code>user</code> variable in both the
<code>name.xml</code> and <code>contact.xml</code> layout files.
</p>
-
+<p>
+ Data binding does not support include as a direct child of a merge element. For example,
+ <strong>the following layout is not supported:</strong>
+</p>
+<pre>
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bind="http://schemas.android.com/apk/res-auto">
+ <data>
+ <variable name="user" type="com.example.User"/>
+ </data>
+ <merge>
+ <include layout="@layout/name"
+ bind:user="@{user}"/>
+ <include layout="@layout/contact"
+ bind:user="@{user}"/>
+ </merge>
+</layout>
+</pre>
<h3 id="expression_language">
Expression Language
</h3>
@@ -613,10 +680,10 @@
</li>
<li>
- <code>L</code>ogical <strong><code>&& ||</code></strong>
+ Logical <strong><code>&& ||</code></strong>
</li>
- <li>Binary <strong><code>&</code> <code>|</code> <code>^</code></strong>
+ <li>Binary <strong><code>& | ^</code></strong>
</li>
<li>Unary <strong><code>+ - ! ~</code></strong>
@@ -659,9 +726,9 @@
</p>
<pre>
-<strong>android:text="@{String.valueOf(index + 1)}"
+android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age &lt; 13 ? View.GONE : View.VISIBLE}"
-android:transitionName='@{"image_" + id}'</strong>
+android:transitionName='@{"image_" + id}'
</pre>
<h4 id="missing_operations">
Missing Operations
@@ -746,23 +813,23 @@
</p>
<pre>
-<<strong>data</strong>>
- <<strong>import type="android.util.SparseArray"</strong>/>
- <<strong>import type="java.util.Map"</strong>/>
- <<strong>import type="java.util.List"</strong>/>
- <<strong>variable name="list" type="List<String>"</strong>/>
- <<strong>variable name="sparse" type="SparseArray&lt;String>"</strong>/>
- <<strong>variable name="map" type="Map&lt;String, String>"</strong>/>
- <<strong>variable name="index" type="int"</strong>/>
- <<strong>variable name="key" type="String"</strong>/>
-</<strong>data</strong>>
+<data>
+ <import type="android.util.SparseArray"/>
+ <import type="java.util.Map"/>
+ <import type="java.util.List"/>
+ <variable name="list" type="List&lt;String>"/>
+ <variable name="sparse" type="SparseArray&lt;String>"/>
+ <variable name="map" type="Map&lt;String, String>"/>
+ <variable name="index" type="int"/>
+ <variable name="key" type="String"/>
+</data>
…
-<strong>android:text="@{list[index]}"
-</strong>…
-<strong>android:text="@{sparse[index]}"
-</strong>…
-<strong>android:text="@{map[key]}"
-</strong>
+android:text="@{list[index]}"
+…
+android:text="@{sparse[index]}"
+…
+android:text="@{map[key]}"
+
</pre>
<h4 id="string_literals">
String Literals
@@ -774,7 +841,7 @@
</p>
<pre>
-<strong>android:text='@{map["firstName"]}'</strong>
+android:text='@{map["firstName"]}'
</pre>
<p>
It is also possible to use double quotes to surround the attribute value.
@@ -783,8 +850,8 @@
</p>
<pre>
-<strong>android:text="@{map[`firstName`}"
-android:text="@{map[&quot;firstName&quot;]}"</strong>
+android:text="@{map[`firstName`}"
+android:text="@{map[&quot;firstName&quot;]}"
</pre>
<h4 id="resources">
Resources
@@ -796,15 +863,15 @@
</p>
<pre>
-<strong>android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"</strong>
+android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
</pre>
<p>
Format strings and plurals may be evaluated by providing parameters:
</p>
<pre>
-<strong>android:text="@{@string/nameFormat(firstName, lastName)}"
-android:text="@{@plurals/banana(bananaCount)}"</strong>
+android:text="@{@string/nameFormat(firstName, lastName)}"
+android:text="@{@plurals/banana(bananaCount)}"
</pre>
<p>
When a plural takes multiple parameters, all parameters should be passed:
@@ -815,7 +882,7 @@
Have an orange
Have %d oranges
-android:text="<strong>@{@plurals/orange(orangeCount, orangeCount)}</strong>"
+android:text="@{@plurals/orange(orangeCount, orangeCount)}"
</pre>
<p>
Some resources require explicit type evaluation.
@@ -836,9 +903,7 @@
<tr>
<td>
- <pre>
-String[]
-</pre>
+ String[]
</td>
<td>
@array
@@ -901,9 +966,7 @@
color <code>int</code>
</td>
<td>
- <pre>
-@color
-</pre>
+ @color
</td>
<td>
@color
@@ -932,8 +995,9 @@
a POJO will not cause the UI to update. The real power of data binding can be
used by giving your data objects the ability to notify when data changes.
There are three different data change notification mechanisms,
- <code>Observable</code> objects, <code>ObservableField</code>s, and
- <code>observable collections</code>.
+ <a href="#observable_objects">Observable objects</a>,
+ <a href="#observablefields">observable fields</a>, and
+ <a href="#observable_collections">observable collection</a>s.
</p>
<p>
@@ -946,49 +1010,49 @@
</h3>
<p>
- A class implementing <code>android.databinding.Observable</code> interface
+ A class implementing the {@link android.databinding.Observable} interface
will allow the binding to attach a single listener to a bound object to
listen for changes of all properties on that object.
</p>
<p>
- The <code>Observable</code> interface has a mechanism to add and remove
+ The {@link android.databinding.Observable} interface has a mechanism to add and remove
listeners, but notifying is up to the developer. To make development easier,
- a base class, <code>BaseObservable,</code> was created to implement the
+ a base class, {@link android.databinding.BaseObservable}, was created to implement the
listener registration mechanism. The data class implementer is still
responsible for notifying when the properties change. This is done by
- assigning a <code>Bindable</code> annotation to the getter and notifying in
+ assigning a {@link android.databinding.Bindable} annotation to the getter and notifying in
the setter.
</p>
<pre>
-<strong>private static class </strong>User <strong>extends </strong>BaseObservable {
- <strong>private </strong>String <strong>firstName</strong>;
- <strong>private </strong>String <strong>lastName</strong>;
+private static class User extends BaseObservable {
+ private String firstName;
+ private String lastName;
@Bindable
- <strong>public </strong>String getFirstName() {
- <strong>return this</strong>.<strong>firstName</strong>;
+ public String getFirstName() {
+ return this.firstName;
}
@Bindable
- <strong>public </strong>String getFirstName() {
- <strong>return this</strong>.<strong>lastName</strong>;
+ public String getLastName() {
+ return this.lastName;
}
- <strong>public void </strong>setFirstName(String firstName) {
- <strong>this</strong>.<strong>firstName </strong>= firstName;
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
- <strong>public void </strong>setLastName(String lastName) {
- <strong>this</strong>.<strong>lastName </strong>= lastName;
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}
</pre>
<p>
- The <code>Bindable</code> annotation generates an entry in the BR class file
+ The {@link android.databinding.Bindable} annotation generates an entry in the BR class file
during compilation. The BR class file will be generated in the module
- package.If the base class for data classes cannot be changed, the
- <code>Observable</code> interface may be implemented using the convenient
- <code>PropertyChangeRegistry</code> to store and notify listeners
+ package. If the base class for data classes cannot be changed, the
+ {@link android.databinding.Observable} interface may be implemented using the convenient
+ {@link android.databinding.PropertyChangeRegistry} to store and notify listeners
efficiently.
</p>
@@ -997,20 +1061,30 @@
</h3>
<p>
- A little work is involved in creating Observable classes, so developers who
- want to save time or have few properties may use ObservableFields.
- ObservableFields are self-contained observable objects that have a single
- field. There are versions for all primitive types and one for reference
- types. To use, create a public final field in the data class:
+ A little work is involved in creating {@link android.databinding.Observable} classes, so
+ developers who want to save time or have few properties may use
+ {@link android.databinding.ObservableField} and its siblings
+ {@link android.databinding.ObservableBoolean},
+ {@link android.databinding.ObservableByte},
+ {@link android.databinding.ObservableChar},
+ {@link android.databinding.ObservableShort},
+ {@link android.databinding.ObservableInt},
+ {@link android.databinding.ObservableLong},
+ {@link android.databinding.ObservableFloat},
+ {@link android.databinding.ObservableDouble}, and
+ {@link android.databinding.ObservableParcelable}.
+ <code>ObservableFields</code> are self-contained observable objects that have a single
+ field. The primitive versions avoid boxing and unboxing during access operations.
+ To use, create a public final field in the data class:
</p>
<pre>
-<strong>private static class </strong>User <strong>extends </strong>BaseObservable {
- <strong>public final </strong>ObservableField<String> <strong>firstName </strong>=
- <strong>new </strong>ObservableField<>();
- <strong>public final </strong>ObservableField<String> <strong>lastName </strong>=
- <strong>new </strong>ObservableField<>();
- <strong>public final </strong>ObservableInt <strong>age </strong>= <strong>new </strong>ObservableInt();
+private static class User {
+ public final ObservableField<String> firstName =
+ new ObservableField<>();
+ public final ObservableField<String> lastName =
+ new ObservableField<>();
+ public final ObservableInt age = new ObservableInt();
}
</pre>
<p>
@@ -1018,8 +1092,8 @@
</p>
<pre>
-user.<strong>firstName</strong>.set(<strong>"Google"</strong>);
-<strong>int </strong>age = user.<strong>age</strong>.get();
+user.firstName.set("Google");
+int age = user.age.get();
</pre>
<h3 id="observable_collections">
Observable Collections
@@ -1027,43 +1101,44 @@
<p>
Some applications use more dynamic structures to hold data. Observable
- collections allow keyed access to these data objects.ObservableArrayMap is
+ collections allow keyed access to these data objects.
+ {@link android.databinding.ObservableArrayMap} is
useful when the key is a reference type, such as String.
</p>
<pre>
-ObservableArrayMap<String, Object> user = <strong>new </strong>ObservableArrayMap<>();
-user.put(<strong>"firstName"</strong>, <strong>"Google"</strong>);
-user.put(<strong>"lastName"</strong>, <strong>"Inc."</strong>);
-user.put(<strong>"age"</strong>, 17);
+ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
+user.put("firstName", "Google");
+user.put("lastName", "Inc.");
+user.put("age", 17);
</pre>
<p>
In the layout, the map may be accessed through the String keys:
</p>
<pre>
-<<strong>data</strong>>
- <<strong>import type="android.databinding.ObservableMap"</strong>/>
- <<strong>variable name="user" type="ObservableMap&lt;String, Object>"</strong>/>
-</<strong>data</strong>>
+<data>
+ <import type="android.databinding.ObservableMap"/>
+ <variable name="user" type="ObservableMap&lt;String, Object>"/>
+</data>
…
-<<strong>TextView
+<TextView
android:text='@{user["lastName"]}'
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/>
-<<strong>TextView
+ android:layout_height="wrap_content"/>
+<TextView
android:text='@{String.valueOf(1 + (Integer)user["age"])}'
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/>
+ android:layout_height="wrap_content"/>
</pre>
<p>
- ObservableArrayList is useful when the key is an integer:
+ {@link android.databinding.ObservableArrayList} is useful when the key is an integer:
</p>
<pre>
-ObservableArrayList<Object> user = <strong>new </strong>ObservableArrayList<>();
-user.add(<strong>"Google"</strong>);
-user.add(<strong>"Inc."</strong>);
+ObservableArrayList<Object> user = new ObservableArrayList<>();
+user.add("Google");
+user.add("Inc.");
user.add(17);
</pre>
<p>
@@ -1071,20 +1146,20 @@
</p>
<pre>
-<<strong>data</strong>>
- <<strong>import type="android.databinding.ObservableList"</strong>/>
- <<strong>import type="com.example.my.app.Fields"</strong>/>
- <<strong>variable name="user" type="ObservableList&lt;Object>"</strong>/>
-</<strong>data</strong>>
+<data>
+ <import type="android.databinding.ObservableList"/>
+ <import type="com.example.my.app.Fields"/>
+ <variable name="user" type="ObservableList&lt;Object>"/>
+</data>
…
-<<strong>TextView
+<TextView
android:text='@{user[Fields.LAST_NAME]}'
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/>
-<<strong>TextView
+ android:layout_height="wrap_content"/>
+<TextView
android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/>
+ android:layout_height="wrap_content"/>
</pre>
<h2 id="generated_binding">
Generated Binding
@@ -1094,7 +1169,7 @@
The generated binding class links the layout variables with the Views within
the layout. As discussed earlier, the name and package of the Binding may be
<a href="#custom_binding_class_names">customized</a>. The Generated binding
- classes all extend <code>android.databinding.ViewDataBinding</code>.
+ classes all extend {@link android.databinding.ViewDataBinding}.
</p>
<h3 id="creating">
@@ -1107,13 +1182,13 @@
within the layout. There are a few ways to bind to a layout. The most common
is to use the static methods on the Binding class.The inflate method inflates
the View hierarchy and binds to it all it one step. There is a simpler
- version that only takes a <code>LayoutInflater</code> and one that takes a
- <code>ViewGroup</code> as well:
+ version that only takes a {@link android.view.LayoutInflater} and one that takes a
+ {@link android.view.ViewGroup} as well:
</p>
<pre>
-MyLayoutBinding binding = MyLayoutBinding.<em>inflate</em>(<strong>layoutInflater</strong>);
-MyLayoutBinding binding = MyLayoutBinding.<em>inflate</em>(LayoutInflater, viewGroup, false);
+MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
+MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);
</pre>
<p>
If the layout was inflated using a different mechanism, it may be bound
@@ -1121,17 +1196,17 @@
</p>
<pre>
-MyLayoutBinding binding = MyLayoutBinding.<em>bind</em>(viewRoot);
+MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
</pre>
<p>
Sometimes the binding cannot be known in advance. In such cases, the binding
- can be created using the DataBindingUtil class:
+ can be created using the {@link android.databinding.DataBindingUtil} class:
</p>
<pre>
-ViewDataBinding binding = DataBindingUtil.<em>inflate</em>(LayoutInflater, layoutId,
+ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
parent, attachToParent);
-ViewDataBinding binding = DataBindingUtil.<em>bindTo</em>(viewRoot, layoutId);
+ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
</pre>
<h3 id="views_with_ids">
Views With IDs
@@ -1145,32 +1220,32 @@
</p>
<pre>
-<<strong>layout xmlns:android="http://schemas.android.com/apk/res/android"</strong>>
- <<strong>data</strong>>
- <<strong>variable name="user" type="com.example.User"</strong>/>
- </<strong>data</strong>>
- <<strong>LinearLayout
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+ <data>
+ <variable name="user" type="com.example.User"/>
+ </data>
+ <LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent"</strong>>
- <<strong>TextView android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+ <TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@{user.firstName}"</strong>
- <strong>android:id="@+id/firstName"</strong>/>
- <<strong>TextView android:layout_width="wrap_content"
+ android:text="@{user.firstName}"
+ android:id="@+id/firstName"/>
+ <TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@{user.lastName}"</strong>
- <strong>android:id="@+id/lastName"</strong>/>
- </<strong>LinearLayout</strong>>
-</<strong>layout</strong>>
+ android:text="@{user.lastName}"
+ android:id="@+id/lastName"/>
+ </LinearLayout>
+</layout>
</pre>
<p>
Will generate a binding class with:
</p>
<pre>
-<strong>public final </strong>TextView <strong>firstName</strong>;
-<strong>public final </strong>TextView <strong>lastName</strong>;
+public final TextView firstName;
+public final TextView lastName;
</pre>
<p>
IDs are not nearly as necessary as without data binding, but there are still
@@ -1186,49 +1261,49 @@
</p>
<pre>
-<<strong>data</strong>>
- <<strong>import type="android.graphics.drawable.Drawable"</strong>/>
- <<strong>variable name="user" type="com.example.User"</strong>/>
- <<strong>variable name="image" type="Drawable"</strong>/>
- <<strong>variable name="note" type="String"</strong>/>
-</<strong>data</strong>>
+<data>
+ <import type="android.graphics.drawable.Drawable"/>
+ <variable name="user" type="com.example.User"/>
+ <variable name="image" type="Drawable"/>
+ <variable name="note" type="String"/>
+</data>
</pre>
<p>
will generate setters and getters in the binding:
</p>
<pre>
-<strong>public abstract </strong>com.example.User getUser();
-<strong>public abstract void </strong>setUser(com.example.User user);
-<strong>public abstract </strong>Drawable getImage();
-<strong>public abstract void </strong>setImage(Drawable image);
-<strong>public abstract </strong>String getNote();
-<strong>public abstract void </strong>setNote(String note);
+public abstract com.example.User getUser();
+public abstract void setUser(com.example.User user);
+public abstract Drawable getImage();
+public abstract void setImage(Drawable image);
+public abstract String getNote();
+public abstract void setNote(String note);
</pre>
<h3 id="viewstubs">
ViewStubs
</h3>
<p>
- ViewStubs are a little different from normal Views. They start off invisible
+ {@link android.view.ViewStub}s are a little different from normal Views. They start off invisible
and when they either are made visible or are explicitly told to inflate, they
replace themselves in the layout by inflating another layout.
</p>
<p>
- Because the ViewStub essentially disappears from the View hierarchy, the View
+ Because the <code>ViewStub</code> essentially disappears from the View hierarchy, the View
in the binding object must also disappear to allow collection. Because the
- Views are final, a ViewStubProxy object takes the place of the ViewStub,
- giving the developer access to the ViewStub when it exists and also access to
- the inflated View hierarchy when the ViewStub has been inflated.
+ Views are final, a {@link android.databinding.ViewStubProxy} object takes the place of the
+ <code>ViewStub</code>, giving the developer access to the ViewStub when it exists and also
+ access to the inflated View hierarchy when the <code>ViewStub</code> has been inflated.
</p>
<p>
When inflating another layout, a binding must be established for the new
- layout. Therefore, the ViewStubProxy must listen to the ViewStub's
- OnInflateListener and establish the binding at that time. Since only one can
- exist, the ViewStubProxy allows the developer to set an OnInflateListener on
- it that it will call after establishing the binding.
+ layout. Therefore, the <code>ViewStubProxy</code> must listen to the <code>ViewStub</code>'s
+ {@link android.view.ViewStub.OnInflateListener} and establish the binding at that time. Since
+ only one can exist, the <code>ViewStubProxy</code> allows the developer to set an
+ <code>OnInflateListener</code> on it that it will call after establishing the binding.
</p>
<h3 id="advanced_binding">
@@ -1241,20 +1316,20 @@
<p>
At times, the specific binding class won't be known. For example, a
- RecyclerView Adapter operating against arbitrary layouts won't know the
- specific binding class. It still must assign the binding value during the
- onBindViewHolder.
+ {@link android.support.v7.widget.RecyclerView.Adapter} operating against arbitrary layouts
+ won't know the specific binding class. It still must assign the binding value during the
+ {@link android.support.v7.widget.RecyclerView.Adapter#onBindViewHolder}.
</p>
<p>
In this example, all layouts that the RecyclerView binds to have an "item"
- variable. The BindingHolder has a getBinding method returning the
- <code>ViewDataBinding</code> base.
+ variable. The <code>BindingHolder</code> has a <code>getBinding</code> method returning the
+ {@link android.databinding.ViewDataBinding} base.
</p>
<pre>
-<strong>public void </strong>onBindViewHolder(BindingHolder holder, <strong>int </strong>position) {
- <strong>final </strong>T item = <strong>mItems</strong>.get(position);
+public void onBindViewHolder(BindingHolder holder, int position) {
+ final T item = mItems.get(position);
holder.getBinding().setVariable(BR.item, item);
holder.getBinding().executePendingBindings();
}
@@ -1267,7 +1342,7 @@
When a variable or observable changes, the binding will be scheduled to
change before the next frame. There are times, however, when binding must be
executed immediately. To force execution, use the
- <code>executePendingBindings()</code> method.
+ {@link android.databinding.ViewDataBinding#executePendingBindings()} method.
</p>
<h4>
@@ -1321,17 +1396,18 @@
<p>
Some attributes have setters that don't match by name. For these
methods, an attribute may be associated with the setter through
- BindingMethods annotation. This must be associated with a class and contains
- BindingMethod annotations, one for each renamed method. For example, the
- <strong><code>android:tint</code></strong> attribute is really associated
- with setImageTintList, not setTint.
+ {@link android.databinding.BindingMethods} annotation. This must be associated with
+ a class and contains {@link android.databinding.BindingMethod} annotations, one for
+ each renamed method. For example, the <strong><code>android:tint</code></strong> attribute
+ is really associated with {@link android.widget.ImageView#setImageTintList}, not
+ <code>setTint</code>.
</p>
<pre>
@BindingMethods({
- @BindingMethod(type = <strong>"android.widget.ImageView"</strong>,
- attribute = <strong>"android:tint"</strong>,
- method = <strong>"setImageTintList"</strong>),
+ @BindingMethod(type = "android.widget.ImageView",
+ attribute = "android:tint",
+ method = "setImageTintList"),
})
</pre>
<p>
@@ -1347,7 +1423,7 @@
Some attributes need custom binding logic. For example, there is no
associated setter for the <strong><code>android:paddingLeft</code></strong>
attribute. Instead, <code>setPadding(left, top, right, bottom)</code> exists.
- A static binding adapter method with the <code>BindingAdapter</code>
+ A static binding adapter method with the {@link android.databinding.BindingAdapter}
annotation allows the developer to customize how a setter for an attribute is
called.
</p>
@@ -1358,9 +1434,8 @@
</p>
<pre>
-
-@BindingAdapter(<strong>"android:paddingLeft"</strong>)
-<strong>public static void </strong>setPaddingLeft(View view, <strong>int </strong>padding) {
+@BindingAdapter("android:paddingLeft")
+public static void setPaddingLeft(View view, int padding) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
@@ -1382,9 +1457,9 @@
</p>
<pre>
-@BindingAdapter({<strong>"bind:imageUrl"</strong>, <strong>"bind:error"</strong>})
-<strong>public static void </strong>loadImage(ImageView view, String url, Drawable error) {
- Picasso.<em>with</em>(view.getContext()).load(url).error(error).into(view);
+@BindingAdapter({"bind:imageUrl", "bind:error"})
+public static void loadImage(ImageView view, String url, Drawable error) {
+ Picasso.with(view.getContext()).load(url).error(error).into(view);
}
</pre>
<pre>
@@ -1406,6 +1481,123 @@
</li>
</ul>
+<p>
+ Binding adapter methods may optionally take the old values in their handlers. A method
+ taking old and new values should have all old values for the attributes come first, followed
+ by the new values:
+</p>
+<pre>
+@BindingAdapter("android:paddingLeft")
+public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
+ if (oldPadding != newPadding) {
+ view.setPadding(newPadding,
+ view.getPaddingTop(),
+ view.getPaddingRight(),
+ view.getPaddingBottom());
+ }
+}
+</pre>
+<p>
+ Event handlers may only be used with interfaces or abstract classes with one abstract method.
+ For example:
+</p>
+<pre>
+@BindingAdapter("android:onLayoutChange")
+public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
+ View.OnLayoutChangeListener newValue) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ if (oldValue != null) {
+ view.removeOnLayoutChangeListener(oldValue);
+ }
+ if (newValue != null) {
+ view.addOnLayoutChangeListener(newValue);
+ }
+ }
+}
+</pre>
+<p>
+ When a listener has multiple methods, it must be split into multiple listeners. For example,
+ {@link android.view.View.OnAttachStateChangeListener} has two methods:
+ {@link android.view.View.OnAttachStateChangeListener#onViewAttachedToWindow onViewAttachedToWindow()} and
+ {@link android.view.View.OnAttachStateChangeListener#onViewDetachedFromWindow onViewDetachedFromWindow()}.
+ We must then create two interfaces to differentiate the attributes and handlers for them.
+</p>
+
+<pre>
+@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
+public interface OnViewDetachedFromWindow {
+ void onViewDetachedFromWindow(View v);
+}
+
+@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
+public interface OnViewAttachedToWindow {
+ void onViewAttachedToWindow(View v);
+}
+</pre>
+<p>
+ Because changing one listener will also affect the other, we must have three different
+ binding adapters, one for each attribute and one for both, should they both be set.
+</p>
+<pre>
+@BindingAdapter("android:onViewAttachedToWindow")
+public static void setListener(View view, OnViewAttachedToWindow attached) {
+ setListener(view, null, attached);
+}
+
+@BindingAdapter("android:onViewDetachedFromWindow")
+public static void setListener(View view, OnViewDetachedFromWindow detached) {
+ setListener(view, detached, null);
+}
+
+@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
+public static void setListener(View view, final OnViewDetachedFromWindow detach,
+ final OnViewAttachedToWindow attach) {
+ if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
+ final OnAttachStateChangeListener newListener;
+ if (detach == null && attach == null) {
+ newListener = null;
+ } else {
+ newListener = new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ if (attach != null) {
+ attach.onViewAttachedToWindow(v);
+ }
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ if (detach != null) {
+ detach.onViewDetachedFromWindow(v);
+ }
+ }
+ };
+ }
+ final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
+ newListener, R.id.onAttachStateChangeListener);
+ if (oldListener != null) {
+ view.removeOnAttachStateChangeListener(oldListener);
+ }
+ if (newListener != null) {
+ view.addOnAttachStateChangeListener(newListener);
+ }
+ }
+}
+</pre>
+<p>
+ The above example is slightly more complicated than normal because View uses add and remove
+ for the listener instead of a set method for {@link android.view.View.OnAttachStateChangeListener}.
+ The <code>android.databinding.adapters.ListenerUtil</code> class helps keep track of the previous
+ listeners so that they may be removed in the Binding Adaper.
+</p>
+<p>
+ By annotating the interfaces <code>OnViewDetachedFromWindow</code> and
+ <code>OnViewAttachedToWindow</code> with
+ <code>@TargetApi(VERSION_CODES.HONEYCOMB_MR1)</code>, the data binding code
+ generator knows that the listener should only be generated when running on Honeycomb MR1
+ and new devices, the same version supported by
+ {@link android.view.View#addOnAttachStateChangeListener}.
+</p>
<h2 id="converters">
Converters
</h2>
@@ -1426,10 +1618,10 @@
</p>
<pre>
-<<strong>TextView
+<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/>
+ android:layout_height="wrap_content"/>
</pre>
<p>
@@ -1447,10 +1639,10 @@
</p>
<pre>
-<<strong>View
+<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/>
+ android:layout_height="wrap_content"/>
</pre>
<p>
Here, the background takes a <code>Drawable</code>, but the color is an
@@ -1462,8 +1654,8 @@
<pre>
@BindingConversion
-<strong>public static </strong>ColorDrawable convertColorToDrawable(<strong>int </strong>color) {
- <strong>return new </strong>ColorDrawable(color);
+public static ColorDrawable convertColorToDrawable(int color) {
+ return new ColorDrawable(color);
}
</pre>
<p>
@@ -1472,8 +1664,8 @@
</p>
<pre>
-<<strong>View
+<View
android:background="@{isError ? @drawable/error : @color/white}"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"</strong>/>
+ android:layout_height="wrap_content"/>
</pre>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index a489f94..0fe5509 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -56,7 +56,7 @@
public class CaptivePortalLoginActivity extends Activity {
private static final String TAG = "CaptivePortalLogin";
- private static final String DEFAULT_SERVER = "connectivitycheck.android.com";
+ private static final String DEFAULT_SERVER = "connectivitycheck.gstatic.com";
private static final int SOCKET_TIMEOUT_MS = 10000;
private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b098258..ec185eb 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -111,7 +111,8 @@
private static final int MSG_DEVICE_PROVISIONED = 308;
private static final int MSG_DPM_STATE_CHANGED = 309;
private static final int MSG_USER_SWITCHING = 310;
- private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312;
+ private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 311;
+ private static final int MSG_KEYGUARD_RESET = 312;
private static final int MSG_BOOT_COMPLETED = 313;
private static final int MSG_USER_SWITCH_COMPLETE = 314;
private static final int MSG_USER_INFO_CHANGED = 317;
@@ -135,6 +136,7 @@
private boolean mKeyguardIsVisible;
private boolean mBouncer;
private boolean mBootCompleted;
+ private boolean mUserHasAuthenticatedSinceBoot;
// Device provisioning state
private boolean mDeviceProvisioned;
@@ -194,6 +196,9 @@
case MSG_KEYGUARD_VISIBILITY_CHANGED:
handleKeyguardVisibilityChanged(msg.arg1);
break;
+ case MSG_KEYGUARD_RESET:
+ handleKeyguardReset();
+ break;
case MSG_KEYGUARD_BOUNCER_CHANGED:
handleKeyguardBouncerChanged(msg.arg1);
break;
@@ -497,7 +502,8 @@
}
public boolean getUserCanSkipBouncer(int userId) {
- return getUserHasTrust(userId) || mUserFingerprintAuthenticated.get(userId);
+ return getUserHasTrust(userId) || (mUserFingerprintAuthenticated.get(userId)
+ && isUnlockingWithFingerprintAllowed());
}
public boolean getUserHasTrust(int userId) {
@@ -508,6 +514,10 @@
return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId);
}
+ public boolean isUnlockingWithFingerprintAllowed() {
+ return mUserHasAuthenticatedSinceBoot;
+ }
+
static class DisplayClientState {
public int clientGeneration;
public boolean clearing;
@@ -867,14 +877,15 @@
}
private boolean shouldListenForFingerprint() {
- return mKeyguardIsVisible && !mSwitchingUser
- && mTrustManager.hasUserAuthenticatedSinceBoot(ActivityManager.getCurrentUser());
+ return mKeyguardIsVisible && !mSwitchingUser;
}
private void startListeningForFingerprint() {
if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
int userId = ActivityManager.getCurrentUser();
if (isUnlockWithFingerPrintPossible(userId)) {
+ mUserHasAuthenticatedSinceBoot = mTrustManager.hasUserAuthenticatedSinceBoot(
+ ActivityManager.getCurrentUser());
if (mFingerprintCancelSignal != null) {
mFingerprintCancelSignal.cancel();
}
@@ -1168,6 +1179,16 @@
}
/**
+ * Handle {@link #MSG_KEYGUARD_RESET}
+ */
+ private void handleKeyguardReset() {
+ if (DEBUG) Log.d(TAG, "handleKeyguardReset");
+ if (!isUnlockingWithFingerprintAllowed()) {
+ updateFingerprintListeningState();
+ }
+ }
+
+ /**
* Handle {@link #MSG_KEYGUARD_BOUNCER_CHANGED}
* @see #sendKeyguardBouncerChanged(boolean)
*/
@@ -1274,6 +1295,10 @@
message.sendToTarget();
}
+ public void sendKeyguardReset() {
+ mHandler.obtainMessage(MSG_KEYGUARD_RESET).sendToTarget();
+ }
+
/**
* @see #handleKeyguardBouncerChanged(int)
*/
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c01a485..7d72dab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -460,13 +460,20 @@
@Override
public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
+ boolean unlockingWithFingerprintAllowed =
+ mUpdateMonitor.isUnlockingWithFingerprintAllowed();
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated();
+ if (unlockingWithFingerprintAllowed) {
+ mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated();
+ }
} else {
- if (wakeAndUnlocking) {
+ if (wakeAndUnlocking && unlockingWithFingerprintAllowed) {
mWakeAndUnlocking = true;
keyguardDone(true, true);
} else {
+ if (wakeAndUnlocking) {
+ mStatusBarKeyguardViewManager.notifyScreenWakeUpRequested();
+ }
mStatusBarKeyguardViewManager.animateCollapsePanels(
FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index a2e6632..41b37b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1837,7 +1837,7 @@
logUpdate(entry, n);
}
boolean applyInPlace = shouldApplyInPlace(entry, n);
- boolean shouldInterrupt = shouldInterrupt(entry);
+ boolean shouldInterrupt = shouldInterrupt(entry, notification);
boolean alertAgain = alertAgain(entry, n);
entry.notification = notification;
@@ -2009,7 +2009,10 @@
}
protected boolean shouldInterrupt(Entry entry) {
- StatusBarNotification sbn = entry.notification;
+ return shouldInterrupt(entry, entry.notification);
+ }
+
+ protected boolean shouldInterrupt(Entry entry, StatusBarNotification sbn) {
if (mNotificationData.shouldFilterOut(sbn)) {
if (DEBUG) {
Log.d(TAG, "Skipping HUN check for " + sbn.getKey() + " since it's filtered out.");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 4558288..7c08efc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -659,6 +659,9 @@
@Override
public void onFingerprintHelp(int msgId, String helpString) {
+ if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed()) {
+ return;
+ }
mLockIcon.setTransientFpError(true);
mIndicationController.showTransientIndication(helpString,
getResources().getColor(R.color.system_warning_color, null));
@@ -668,6 +671,9 @@
@Override
public void onFingerprintError(int msgId, String errString) {
+ if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed()) {
+ return;
+ }
// TODO: Go to bouncer if this is "too many attempts" (lockout) error.
mIndicationController.showTransientIndication(errString,
getResources().getColor(R.color.system_warning_color, null));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 9e2ce15..d93f7c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -224,13 +224,14 @@
}
private int getState() {
- boolean fingerprintRunning =
- KeyguardUpdateMonitor.getInstance(mContext).isFingerprintDetectionRunning();
+ KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ boolean fingerprintRunning = updateMonitor.isFingerprintDetectionRunning();
+ boolean unlockingAllowed = updateMonitor.isUnlockingWithFingerprintAllowed();
if (mUnlockMethodCache.canSkipBouncer()) {
return STATE_LOCK_OPEN;
} else if (mTransientFpError) {
return STATE_FINGERPRINT_ERROR;
- } else if (fingerprintRunning) {
+ } else if (fingerprintRunning && unlockingAllowed) {
return STATE_FINGERPRINT;
} else if (mUnlockMethodCache.isFaceUnlockRunning()) {
return STATE_FACE_UNLOCK;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index e622144..6b3a59d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -150,6 +150,7 @@
} else {
showBouncerOrKeyguard();
}
+ KeyguardUpdateMonitor.getInstance(mContext).sendKeyguardReset();
updateStates();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index f31311d..c8c45e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -133,6 +133,9 @@
@Override
public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
+ if (!mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()) {
+ return;
+ }
update(false /* updateAlways */);
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 4b0b924..4102f37 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -58,6 +58,11 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
import java.util.List;
/**
@@ -897,7 +902,10 @@
}
final class WakeupReasonThread extends Thread {
- final String[] mReason = new String[1];
+ private static final int MAX_REASON_SIZE = 512;
+ private CharsetDecoder mDecoder;
+ private ByteBuffer mUtf8Buffer;
+ private CharBuffer mUtf16Buffer;
WakeupReasonThread() {
super("BatteryStats_wakeupReason");
@@ -906,25 +914,53 @@
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+ mDecoder = StandardCharsets.UTF_8
+ .newDecoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE)
+ .replaceWith("?");
+
+ mUtf8Buffer = ByteBuffer.allocateDirect(MAX_REASON_SIZE);
+ mUtf16Buffer = CharBuffer.allocate(MAX_REASON_SIZE);
+
try {
- int num;
- while ((num = nativeWaitWakeup(mReason)) >= 0) {
+ String reason;
+ while ((reason = waitWakeup()) != null) {
synchronized (mStats) {
- // num will be either 0 or 1.
- if (num > 0) {
- mStats.noteWakeupReasonLocked(mReason[0]);
- } else {
- mStats.noteWakeupReasonLocked("unknown");
- }
+ mStats.noteWakeupReasonLocked(reason);
}
}
} catch (RuntimeException e) {
Slog.e(TAG, "Failure reading wakeup reasons", e);
}
}
+
+ private String waitWakeup() {
+ mUtf8Buffer.clear();
+ mUtf16Buffer.clear();
+ mDecoder.reset();
+
+ int bytesWritten = nativeWaitWakeup(mUtf8Buffer);
+ if (bytesWritten < 0) {
+ return null;
+ } else if (bytesWritten == 0) {
+ return "unknown";
+ }
+
+ // Set the buffer's limit to the number of bytes written.
+ mUtf8Buffer.limit(bytesWritten);
+
+ // Decode the buffer from UTF-8 to UTF-16.
+ // Unmappable characters will be replaced.
+ mDecoder.decode(mUtf8Buffer, mUtf16Buffer, true);
+ mUtf16Buffer.flip();
+
+ // Create a String from the UTF-16 buffer.
+ return mUtf16Buffer.toString();
+ }
}
- private static native int nativeWaitWakeup(String[] outReason);
+ private static native int nativeWaitWakeup(ByteBuffer outBuffer);
private void dumpHelp(PrintWriter pw) {
pw.println("Battery stats (batterystats) dump options:");
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 2633939..952ba08 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -77,7 +77,7 @@
public class NetworkMonitor extends StateMachine {
private static final boolean DBG = true;
private static final String TAG = "NetworkMonitor";
- private static final String DEFAULT_SERVER = "connectivitycheck.android.com";
+ private static final String DEFAULT_SERVER = "connectivitycheck.gstatic.com";
private static final int SOCKET_TIMEOUT_MS = 10000;
public static final String ACTION_NETWORK_CONDITIONS_MEASURED =
"android.net.conn.NETWORK_CONDITIONS_MEASURED";
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index dc3e2d6..d12481c4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5527,7 +5527,7 @@
private void finishKeyguardDrawn() {
synchronized (mLock) {
- if (!mAwake || mKeyguardDrawComplete) {
+ if (!mScreenOnEarly || mKeyguardDrawComplete) {
return; // We are not awake yet or we have already informed of this event.
}
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index e257e89..dfc5ef6 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -59,9 +59,9 @@
}
}
-static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobjectArray outReasons)
+static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
{
- if (outReasons == NULL) {
+ if (outBuf == NULL) {
jniThrowException(env, "java/lang/NullPointerException", "null argument");
return -1;
}
@@ -99,11 +99,11 @@
return -1;
}
- ALOGV("Reading wakeup reasons");
+ char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf);
+ int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf);
- char mergedreason[MAX_REASON_SIZE];
+ ALOGV("Reading wakeup reasons");
char* mergedreasonpos = mergedreason;
- int remainreasonlen = MAX_REASON_SIZE;
char reasonline[128];
int i = 0;
while (fgets(reasonline, sizeof(reasonline), fp) != NULL) {
@@ -161,21 +161,17 @@
ALOGV("Got %d reasons", i);
if (i > 0) {
*mergedreasonpos = 0;
- ScopedLocalRef<jstring> reasonString(env, env->NewStringUTF(mergedreason));
- env->SetObjectArrayElement(outReasons, 0, reasonString.get());
- i = 1;
}
if (fclose(fp) != 0) {
ALOGE("Failed to close %s", LAST_RESUME_REASON);
return -1;
}
-
- return i;
+ return mergedreasonpos - mergedreason;
}
static JNINativeMethod method_table[] = {
- { "nativeWaitWakeup", "([Ljava/lang/String;)I", (void*)nativeWaitWakeup },
+ { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
};
int register_android_server_BatteryStatsService(JNIEnv *env)