summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt8
-rw-r--r--api/system-current.txt9
-rw-r--r--core/java/android/app/ActivityView.java2
-rw-r--r--core/java/android/app/StatusBarManager.java3
-rw-r--r--core/java/android/content/Intent.java33
-rw-r--r--core/java/android/widget/RemoteViews.java41
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java19
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl4
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java2
-rw-r--r--core/res/AndroidManifest.xml3
-rw-r--r--core/res/res/values-watch/config.xml3
-rw-r--r--core/res/res/values/attrs.xml7
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/styles_micro.xml1
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--docs/html/tools/support-library/index.jd2
-rw-r--r--packages/SystemUI/res/values-en/donottranslate.xml23
-rw-r--r--packages/SystemUI/res/values/donottranslate.xml6
-rw-r--r--packages/SystemUI/res/values/strings.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java2
-rw-r--r--services/core/java/com/android/server/DeviceIdleController.java117
-rw-r--r--services/core/java/com/android/server/GestureLauncherService.java11
-rw-r--r--services/core/java/com/android/server/ThermalObserver.java146
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java7
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java2
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java2
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java4
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java2
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java4
-rw-r--r--services/java/com/android/server/SystemServer.java5
-rw-r--r--services/net/java/android/net/dhcp/DhcpClient.java5
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java9
-rw-r--r--tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java2
-rw-r--r--tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java3
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/BridgeResources.java2
-rw-r--r--tools/layoutlib/bridge/src/android/view/BridgeInflater.java2
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java2
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java378
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java19
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java183
45 files changed, 1072 insertions, 103 deletions
diff --git a/api/current.txt b/api/current.txt
index d7fdb2d..c45cc3d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -45134,7 +45134,10 @@ package java.lang.reflect {
}
public final class Constructor extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+ method public boolean equals(java.lang.Object);
method public A getAnnotation(java.lang.Class<A>);
+ method public java.lang.annotation.Annotation[] getAnnotations();
+ method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public java.lang.Class<T> getDeclaringClass();
method public java.lang.Class<?>[] getExceptionTypes();
method public java.lang.reflect.Type[] getGenericExceptionTypes();
@@ -45144,6 +45147,7 @@ package java.lang.reflect {
method public java.lang.annotation.Annotation[][] getParameterAnnotations();
method public java.lang.Class<?>[] getParameterTypes();
method public java.lang.reflect.TypeVariable<java.lang.reflect.Constructor<T>>[] getTypeParameters();
+ method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
method public boolean isSynthetic();
method public boolean isVarArgs();
method public T newInstance(java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException;
@@ -45217,7 +45221,10 @@ package java.lang.reflect {
}
public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+ method public boolean equals(java.lang.Object);
method public A getAnnotation(java.lang.Class<A>);
+ method public java.lang.annotation.Annotation[] getAnnotations();
+ method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public java.lang.Class<?> getDeclaringClass();
method public java.lang.Object getDefaultValue();
method public java.lang.Class<?>[] getExceptionTypes();
@@ -45231,6 +45238,7 @@ package java.lang.reflect {
method public java.lang.Class<?> getReturnType();
method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
+ method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
method public boolean isBridge();
method public boolean isSynthetic();
method public boolean isVarArgs();
diff --git a/api/system-current.txt b/api/system-current.txt
index f464292..1e7b94e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -32714,6 +32714,7 @@ package android.telecom {
field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
+ field public static final java.lang.String ACTION_PHONE_ACCOUNT_UNREGISTERED = "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
@@ -47751,7 +47752,10 @@ package java.lang.reflect {
}
public final class Constructor extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+ method public boolean equals(java.lang.Object);
method public A getAnnotation(java.lang.Class<A>);
+ method public java.lang.annotation.Annotation[] getAnnotations();
+ method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public java.lang.Class<T> getDeclaringClass();
method public java.lang.Class<?>[] getExceptionTypes();
method public java.lang.reflect.Type[] getGenericExceptionTypes();
@@ -47761,6 +47765,7 @@ package java.lang.reflect {
method public java.lang.annotation.Annotation[][] getParameterAnnotations();
method public java.lang.Class<?>[] getParameterTypes();
method public java.lang.reflect.TypeVariable<java.lang.reflect.Constructor<T>>[] getTypeParameters();
+ method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
method public boolean isSynthetic();
method public boolean isVarArgs();
method public T newInstance(java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException;
@@ -47834,7 +47839,10 @@ package java.lang.reflect {
}
public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
+ method public boolean equals(java.lang.Object);
method public A getAnnotation(java.lang.Class<A>);
+ method public java.lang.annotation.Annotation[] getAnnotations();
+ method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
method public java.lang.Class<?> getDeclaringClass();
method public java.lang.Object getDefaultValue();
method public java.lang.Class<?>[] getExceptionTypes();
@@ -47848,6 +47856,7 @@ package java.lang.reflect {
method public java.lang.Class<?> getReturnType();
method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
+ method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
method public boolean isBridge();
method public boolean isSynthetic();
method public boolean isVarArgs();
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 9c0d931..371c923 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -239,6 +239,8 @@ public class ActivityView extends ViewGroup {
}
mTextureView.setSurfaceTextureListener(null);
+
+ mThread.quit();
}
private void attachToSurfaceWhenReady() {
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 31d1ab7..5e8ad68 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -90,6 +90,9 @@ public class StatusBarManager {
public static final int WINDOW_STATE_HIDING = 1;
public static final int WINDOW_STATE_HIDDEN = 2;
+ public static final int CAMERA_LAUNCH_SOURCE_WIGGLE = 0;
+ public static final int CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = 1;
+
private Context mContext;
private IStatusBarService mService;
private IBinder mToken = new Binder();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 12c2632..6bbee56 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3022,6 +3022,39 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_PROCESS_TEXT_READONLY =
"android.intent.extra.PROCESS_TEXT_READONLY";
+ /**
+ * Broadcast action: reports when a new thermal event has been reached. When the device
+ * is reaching its maximum temperatue, the thermal level reported
+ * {@hide}
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_THERMAL_EVENT = "android.intent.action.THERMAL_EVENT";
+
+ /** {@hide} */
+ public static final String EXTRA_THERMAL_STATE = "android.intent.extra.THERMAL_STATE";
+
+ /**
+ * Thermal state when the device is normal. This state is sent in the
+ * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * {@hide}
+ */
+ public static final int EXTRA_THERMAL_STATE_NORMAL = 0;
+
+ /**
+ * Thermal state where the device is approaching its maximum threshold. This state is sent in
+ * the {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * {@hide}
+ */
+ public static final int EXTRA_THERMAL_STATE_WARNING = 1;
+
+ /**
+ * Thermal state where the device has reached its maximum threshold. This state is sent in the
+ * {@link ACTION_THERMAL_EVENT} broadcast as {@link EXTRA_THERMAL_STATE}.
+ * {@hide}
+ */
+ public static final int EXTRA_THERMAL_STATE_EXCEEDED = 2;
+
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 7ca3339..ca1b211 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -31,6 +31,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.Rect;
@@ -55,6 +56,8 @@ import android.view.ViewGroup;
import android.widget.AdapterView.OnItemClickListener;
import libcore.util.Objects;
+import com.android.internal.R;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -206,14 +209,22 @@ public class RemoteViews implements Parcelable, Filter {
/** @hide */
public static class OnClickHandler {
+
+ private int mEnterAnimationId;
+
public boolean onClickHandler(View view, PendingIntent pendingIntent,
Intent fillInIntent) {
try {
// TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
Context context = view.getContext();
- ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
- 0, 0,
- view.getMeasuredWidth(), view.getMeasuredHeight());
+ ActivityOptions opts;
+ if (mEnterAnimationId != 0) {
+ opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0);
+ } else {
+ opts = ActivityOptions.makeScaleUpAnimation(view,
+ 0, 0,
+ view.getMeasuredWidth(), view.getMeasuredHeight());
+ }
context.startIntentSender(
pendingIntent.getIntentSender(), fillInIntent,
Intent.FLAG_ACTIVITY_NEW_TASK,
@@ -228,6 +239,10 @@ public class RemoteViews implements Parcelable, Filter {
}
return true;
}
+
+ public void setEnterAnimationId(int enterAnimationId) {
+ mEnterAnimationId = enterAnimationId;
+ }
}
/**
@@ -2761,11 +2776,31 @@ public class RemoteViews implements Parcelable, Filter {
inflater.setFilter(this);
result = inflater.inflate(rvToApply.getLayoutId(), parent, false);
+ loadTransitionOverride(context, handler);
+
rvToApply.performApply(result, parent, handler);
return result;
}
+ private static void loadTransitionOverride(Context context,
+ RemoteViews.OnClickHandler handler) {
+ if (handler != null && context.getResources().getBoolean(
+ com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
+ TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
+ com.android.internal.R.styleable.Window);
+ int windowAnimations = windowStyle.getResourceId(
+ com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
+ TypedArray windowAnimationStyle = context.obtainStyledAttributes(
+ windowAnimations, com.android.internal.R.styleable.WindowAnimation);
+ handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
+ com.android.internal.R.styleable.
+ WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
+ windowStyle.recycle();
+ windowAnimationStyle.recycle();
+ }
+ }
+
/**
* Applies all of the actions to the provided view.
*
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e39bf60..64b7768 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -105,7 +105,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 131 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 132 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -5704,6 +5704,8 @@ public final class BatteryStatsImpl extends BatteryStats {
cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in);
}
}
+ } else {
+ mCpuClusterSpeed[cluster] = null;
}
}
} else {
@@ -9382,13 +9384,14 @@ public final class BatteryStatsImpl extends BatteryStats {
u.mCpuClusterSpeed = new LongSamplingCounter[numClusters][];
for (int cluster = 0; cluster < numClusters; cluster++) {
- int NSB = in.readInt();
- if (mPowerProfile != null &&
- mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) {
- throw new ParcelFormatException("File corrupt: too many speed bins " + NSB);
- }
-
if (in.readInt() != 0) {
+ final int NSB = in.readInt();
+ if (mPowerProfile != null &&
+ mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != NSB) {
+ throw new ParcelFormatException("File corrupt: too many speed bins " +
+ NSB);
+ }
+
u.mCpuClusterSpeed[cluster] = new LongSamplingCounter[NSB];
for (int speed = 0; speed < NSB; speed++) {
if (in.readInt() != 0) {
@@ -9397,6 +9400,8 @@ public final class BatteryStatsImpl extends BatteryStats {
u.mCpuClusterSpeed[cluster][speed].readSummaryFromParcelLocked(in);
}
}
+ } else {
+ u.mCpuClusterSpeed[cluster] = null;
}
}
} else {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index feed3903..ba67cf4 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -72,7 +72,9 @@ oneway interface IStatusBar
/**
* Notifies the status bar that a camera launch gesture has been detected.
+ *
+ * @param source the identifier for the gesture, see {@link StatusBarManager}
*/
- void onCameraLaunchGestureDetected();
+ void onCameraLaunchGestureDetected(int source);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 82ae2f3..60380fb 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1074,7 +1074,7 @@ public class LockPatternUtils {
long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId);
final long timeoutMs = getLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0L, userId);
final long now = SystemClock.elapsedRealtime();
- if (deadline < now) {
+ if (deadline < now && deadline != 0) {
// timeout expired
setLong(LOCKOUT_ATTEMPT_DEADLINE, 0, userId);
setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0, userId);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b051de4..0614d2c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -65,6 +65,7 @@
<protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
<protected-broadcast android:name="android.intent.action.REBOOT" />
<protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
+ <protected-broadcast android:name="android.intent.action.THERMAL_EVENT" />
<protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
<protected-broadcast android:name="android.intent.action.USER_ADDED" />
<protected-broadcast android:name="android.intent.action.USER_REMOVED" />
@@ -1706,7 +1707,7 @@
android:protectionLevel="signature|privileged" />
<!-- Allows applications to change network connectivity state.
- <p>Protection level: normal
+ <p>Protection level: signature
-->
<permission android:name="android.permission.CHANGE_NETWORK_STATE"
android:description="@string/permdesc_changeNetworkState"
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 41b05ea..919519e 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -54,4 +54,7 @@
<!-- Do not show the message saying USB is connected in charging mode. -->
<bool name="config_usbChargingMessage">false</bool>
+
+ <!-- Use a custom transition for RemoteViews. -->
+ <bool name="config_overrideRemoteViewsActivityTransition">true</bool>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index cf83422..2828d21 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2144,6 +2144,13 @@ i
(which is exiting the screen). The wallpaper remains
static behind the animation. -->
<attr name="wallpaperIntraCloseExitAnimation" format="reference" />
+
+ <!-- When opening a new activity from a RemoteViews, this is the
+ animation that is run on the next activity (which is entering the
+ screen). Requires config_overrideRemoteViewsActivityTransition to
+ be true. -->
+ <attr name="activityOpenRemoteViewsEnterAnimation" format="reference" />
+
</declare-styleable>
<!-- ============================= -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f1e26aa..0859e5a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2217,6 +2217,10 @@
<bool name="config_defaultWindowFeatureOptionsPanel">true</bool>
<bool name="config_defaultWindowFeatureContextMenu">true</bool>
+ <!-- If true, the transition for a RemoteViews is read from a resource instead of using the
+ default scale-up transition. -->
+ <bool name="config_overrideRemoteViewsActivityTransition">false</bool>
+
<!-- This config is used to check if the carrier requires converting destination
number before sending out a SMS.
Formats for this configuration as below:
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index c6052ff..05835e7 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -18,6 +18,7 @@
<style name="Animation.Micro.Activity" parent="Animation.Material.Activity">
<item name="activityOpenEnterAnimation">@anim/slide_in_micro</item>
+ <item name="activityOpenRemoteViewsEnterAnimation">@anim/slide_in_micro</item>
<item name="activityOpenExitAnimation">@null</item>
<item name="activityCloseEnterAnimation">@null</item>
<item name="activityCloseExitAnimation">@anim/slide_out_micro</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bdbc86f..6d8c38f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2203,6 +2203,7 @@
<java-symbol type="bool" name="config_sms_force_7bit_encoding" />
<java-symbol type="bool" name="config_defaultWindowFeatureOptionsPanel" />
<java-symbol type="bool" name="config_defaultWindowFeatureContextMenu" />
+ <java-symbol type="bool" name="config_overrideRemoteViewsActivityTransition" />
<java-symbol type="layout" name="simple_account_item" />
<java-symbol type="array" name="config_sms_convert_destination_number_support" />
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd
index 9dc0ed1..22ad0c9 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/tools/support-library/index.jd
@@ -665,7 +665,7 @@ page.title=Support Library
<li>Added support for a Collapse icon description in the {@link android.support.v7.widget.Toolbar}
class.</li>
<li>Updated the {@link android.support.v7.widget.SearchView} widget to support displaying
- the {@link android.support.v7.mediarouter.R.attr#commitIcon}. </li>
+ the {@link android.support.v7.appcompat.R.attr#commitIcon}. </li>
<li>Removed the <code>buttonGravity</code> attribute from the
{@link android.support.v7.widget.Toolbar} class. </li>
</ul>
diff --git a/packages/SystemUI/res/values-en/donottranslate.xml b/packages/SystemUI/res/values-en/donottranslate.xml
new file mode 100644
index 0000000..9f04e1f
--- /dev/null
+++ b/packages/SystemUI/res/values-en/donottranslate.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time_fast</item>
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time_slowly</item>
+</resources> \ No newline at end of file
diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml
index 351a1fd..30ff704 100644
--- a/packages/SystemUI/res/values/donottranslate.xml
+++ b/packages/SystemUI/res/values/donottranslate.xml
@@ -20,4 +20,10 @@
<!-- Date format for display: should match the lockscreen in /policy. -->
<string name="system_ui_date_pattern">@*android:string/system_ui_date_pattern</string>
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_fast_if_translated">@string/keyguard_indication_charging_time</item>
+
+ <!-- DO NOT TRANSLATE - temporary hack to show the speed-less label if no translation is available -->
+ <item type="string" name="keyguard_indication_charging_time_slowly_if_translated">@string/keyguard_indication_charging_time</item>
+
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3eac84f..dfa85ce 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -793,6 +793,12 @@
<!-- Indication on the keyguard that is shown when the device is charging. [CHAR LIMIT=40]-->
<string name="keyguard_indication_charging_time">Charging (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+ <!-- Indication on the keyguard that is shown when the device is charging rapidly. Should match keyguard_plugged_in_charging_fast [CHAR LIMIT=40]-->
+ <string name="keyguard_indication_charging_time_fast">Charging rapidly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+
+ <!-- Indication on the keyguard that is shown when the device is charging slowly. Should match keyguard_plugged_in_charging_slowly [CHAR LIMIT=40]-->
+ <string name="keyguard_indication_charging_time_slowly">Charging slowly (<xliff:g id="charging_time_left" example="4 hours and 2 minutes">%s</xliff:g> until full)</string>
+
<!-- Related to user switcher --><skip/>
<!-- Accessibility label for the button that opens the user switcher. -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 025451d..897f5e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -110,7 +110,7 @@ public class CommandQueue extends IStatusBar.Stub {
public void appTransitionStarting(long startTime, long duration);
public void showAssistDisclosure();
public void startAssist(Bundle args);
- public void onCameraLaunchGestureDetected();
+ public void onCameraLaunchGestureDetected(int source);
}
public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -296,10 +296,10 @@ public class CommandQueue extends IStatusBar.Stub {
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
synchronized (mList) {
mHandler.removeMessages(MSG_CAMERA_LAUNCH_GESTURE);
- mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE).sendToTarget();
+ mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE, source, 0).sendToTarget();
}
}
@@ -402,7 +402,7 @@ public class CommandQueue extends IStatusBar.Stub {
mCallbacks.startAssist((Bundle) msg.obj);
break;
case MSG_CAMERA_LAUNCH_GESTURE:
- mCallbacks.onCameraLaunchGestureDetected();
+ mCallbacks.onCameraLaunchGestureDetected(msg.arg1);
break;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 54f91da..50d274d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -187,32 +187,41 @@ public class KeyguardIndicationController {
}
// Try fetching charging time from battery stats.
+ long chargingTimeRemaining = 0;
try {
- long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
- if (chargingTimeRemaining > 0) {
- String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
- mContext, chargingTimeRemaining);
- return mContext.getResources().getString(
- R.string.keyguard_indication_charging_time, chargingTimeFormatted);
- }
+ chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
+
} catch (RemoteException e) {
Log.e(TAG, "Error calling IBatteryStats: ", e);
}
+ final boolean hasChargingTime = chargingTimeRemaining > 0;
- // Fall back to simple charging label.
int chargingId;
switch (mChargingSpeed) {
case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
- chargingId = R.string.keyguard_plugged_in_charging_fast;
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time_fast_if_translated
+ : R.string.keyguard_plugged_in_charging_fast;
break;
case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
- chargingId = R.string.keyguard_plugged_in_charging_slowly;
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time_slowly_if_translated
+ : R.string.keyguard_plugged_in_charging_slowly;
break;
default:
- chargingId = R.string.keyguard_plugged_in;
+ chargingId = hasChargingTime
+ ? R.string.keyguard_indication_charging_time
+ : R.string.keyguard_plugged_in;
break;
}
- return mContext.getResources().getString(chargingId);
+
+ if (hasChargingTime) {
+ String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ mContext, chargingTimeRemaining);
+ return mContext.getResources().getString(chargingId, chargingTimeFormatted);
+ } else {
+ return mContext.getResources().getString(chargingId);
+ }
}
KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
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 012dc9c..14176a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -77,6 +77,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
+ public static final String CAMERA_LAUNCH_SOURCE_AFFORDANCE = "lockscreen_affordance";
+ public static final String CAMERA_LAUNCH_SOURCE_WIGGLE = "wiggle_gesture";
+ public static final String CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP = "power_double_tap";
+
+ public static final String EXTRA_CAMERA_LAUNCH_SOURCE
+ = "com.android.systemui.camera_launch_source";
+
private static final Intent SECURE_CAMERA_INTENT =
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
@@ -170,7 +177,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
return true;
} else if (host == mCameraImageView) {
- launchCamera();
+ launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
return true;
} else if (host == mLeftAffordanceView) {
launchLeftAffordance();
@@ -349,7 +356,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
@Override
public void onClick(View v) {
if (v == mCameraImageView) {
- launchCamera();
+ launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
} else if (v == mLeftAffordanceView) {
launchLeftAffordance();
} if (v == mLockIcon) {
@@ -417,8 +424,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
}
}
- public void launchCamera() {
+ public void launchCamera(String source) {
final Intent intent = getCameraIntent();
+ intent.putExtra(EXTRA_CAMERA_LAUNCH_SOURCE, source);
boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity(
mContext, intent, KeyguardUpdateMonitor.getCurrentUser());
if (intent == SECURE_CAMERA_INTENT && !wouldLaunchResolverActivity) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 9321938..bdd2c73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -22,7 +22,7 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
+import android.app.StatusBarManager;
import android.content.Context;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@@ -203,6 +203,7 @@ public class NotificationPanelView extends PanelView implements
private boolean mClosingWithAlphaFadeOut;
private boolean mHeadsUpAnimatingAway;
private boolean mLaunchingAffordance;
+ private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
@Override
@@ -478,6 +479,7 @@ public class NotificationPanelView extends PanelView implements
mUnlockIconActive = false;
if (!mLaunchingAffordance) {
mAfforanceHelper.reset(false);
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
}
closeQs();
mStatusBar.dismissPopups();
@@ -1943,9 +1945,13 @@ public class NotificationPanelView extends PanelView implements
EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_DIALER, lengthDp, velocityDp);
mKeyguardBottomArea.launchLeftAffordance();
} else {
- EventLogTags.writeSysuiLockscreenGesture(
- EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA, lengthDp, velocityDp);
- mKeyguardBottomArea.launchCamera();
+ if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
+ mLastCameraLaunchSource)) {
+ EventLogTags.writeSysuiLockscreenGesture(
+ EventLogConstants.SYSUI_LOCKSCREEN_GESTURE_SWIPE_CAMERA,
+ lengthDp, velocityDp);
+ }
+ mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
}
mStatusBar.startLaunchTransitionTimeout();
mBlockTouches = true;
@@ -2383,7 +2389,17 @@ public class NotificationPanelView extends PanelView implements
return !mDozing;
}
- public void launchCamera(boolean animate) {
+ public void launchCamera(boolean animate, int source) {
+ if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
+ } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
+ } else {
+
+ // Default.
+ mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
+ }
+
// If we are launching it when we are occluded already we don't want it to animate,
// nor setting these flags, since the occluded state doesn't change anymore, hence it's
// never reset.
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 2bedef7..8465101 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -489,6 +489,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private ExpandableNotificationRow mDraggedDownRow;
private boolean mLaunchCameraOnScreenTurningOn;
private boolean mLaunchCameraOnFinishedGoingToSleep;
+ private int mLastCameraLaunchSource;
private PowerManager.WakeLock mGestureWakeLock;
private Vibrator mVibrator;
@@ -3936,7 +3937,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mHandler.post(new Runnable() {
@Override
public void run() {
- onCameraLaunchGestureDetected();
+ onCameraLaunchGestureDetected(mLastCameraLaunchSource);
}
});
}
@@ -3953,7 +3954,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mScreenTurningOn = true;
mNotificationPanel.onScreenTurningOn();
if (mLaunchCameraOnScreenTurningOn) {
- mNotificationPanel.launchCamera(false);
+ mNotificationPanel.launchCamera(false, mLastCameraLaunchSource);
mLaunchCameraOnScreenTurningOn = false;
}
}
@@ -4118,7 +4119,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
+ mLastCameraLaunchSource = source;
if (mStartedGoingToSleep) {
mLaunchCameraOnFinishedGoingToSleep = true;
return;
@@ -4144,7 +4146,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
if (mScreenTurningOn || mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
- mNotificationPanel.launchCamera(mDeviceInteractive /* animate */);
+ mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
// we will dismiss us too early since we are waiting on an activity to be drawn and
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 2587b9f..bbe5dd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -171,7 +171,7 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
}
@Override
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 46fd28a..5239ede 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -31,6 +31,8 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.hardware.display.DisplayManager;
@@ -111,7 +113,7 @@ public class DeviceIdleController extends SystemService
private INetworkPolicyManager mNetworkPolicyManager;
private DisplayManager mDisplayManager;
private SensorManager mSensorManager;
- private Sensor mSigMotionSensor;
+ private Sensor mMotionSensor;
private LocationManager mLocationManager;
private LocationRequest mLocationRequest;
private PendingIntent mSensingAlarmIntent;
@@ -123,7 +125,6 @@ public class DeviceIdleController extends SystemService
private boolean mForceIdle;
private boolean mScreenOn;
private boolean mCharging;
- private boolean mSigMotionActive;
private boolean mSensing;
private boolean mNotMoving;
private boolean mLocating;
@@ -268,13 +269,57 @@ public class DeviceIdleController extends SystemService
}
};
- private final TriggerEventListener mSigMotionListener = new TriggerEventListener() {
- @Override public void onTrigger(TriggerEvent event) {
+ private final class MotionListener extends TriggerEventListener
+ implements SensorEventListener {
+
+ boolean active = false;
+
+ @Override
+ public void onTrigger(TriggerEvent event) {
synchronized (DeviceIdleController.this) {
- significantMotionLocked();
+ active = false;
+ motionLocked();
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ synchronized (DeviceIdleController.this) {
+ mSensorManager.unregisterListener(this, mMotionSensor);
+ active = false;
+ motionLocked();
}
}
- };
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+ public boolean registerLocked() {
+ boolean success = false;
+ if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ success = mSensorManager.requestTriggerSensor(mMotionListener, mMotionSensor);
+ } else {
+ success = mSensorManager.registerListener(
+ mMotionListener, mMotionSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ if (success) {
+ active = true;
+ } else {
+ Slog.e(TAG, "Unable to register for " + mMotionSensor);
+ }
+ return success;
+ }
+
+ public void unregisterLocked() {
+ if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ mSensorManager.cancelTriggerSensor(mMotionListener, mMotionSensor);
+ } else {
+ mSensorManager.unregisterListener(mMotionListener);
+ }
+ active = false;
+ }
+ }
+ private final MotionListener mMotionListener = new MotionListener();
private final LocationListener mGenericLocationListener = new LocationListener() {
@Override
@@ -349,7 +394,7 @@ public class DeviceIdleController extends SystemService
* This is the time, after becoming inactive, at which we start looking at the
* motion sensor to determine if the device is being left alone. We don't do this
* immediately after going inactive just because we don't want to be continually running
- * the significant motion sensor whenever the screen is off.
+ * the motion sensor whenever the screen is off.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_INACTIVE_TIMEOUT
*/
@@ -392,7 +437,7 @@ public class DeviceIdleController extends SystemService
/**
* This is the time, after the inactive timeout elapses, that we will wait looking
- * for significant motion until we truly consider the device to be idle.
+ * for motion until we truly consider the device to be idle.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
*/
@@ -886,18 +931,19 @@ public class DeviceIdleController extends SystemService
int sigMotionSensorId = getContext().getResources().getInteger(
com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);
if (sigMotionSensorId > 0) {
- mSigMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
+ mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
}
- if (mSigMotionSensor == null && getContext().getResources().getBoolean(
+ if (mMotionSensor == null && getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {
- mSigMotionSensor = mSensorManager.getDefaultSensor(
- Sensor.TYPE_WRIST_TILT_GESTURE);
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_WRIST_TILT_GESTURE, true);
}
- if (mSigMotionSensor == null) {
+ if (mMotionSensor == null) {
// As a last ditch, fall back to SMD.
- mSigMotionSensor = mSensorManager.getDefaultSensor(
- Sensor.TYPE_SIGNIFICANT_MOTION);
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_SIGNIFICANT_MOTION, true);
}
+
if (getContext().getResources().getBoolean(
com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
mLocationManager = (LocationManager) getContext().getSystemService(
@@ -1242,7 +1288,7 @@ public class DeviceIdleController extends SystemService
cancelAlarmLocked();
cancelSensingAlarmLocked();
cancelLocatingLocked();
- stopMonitoringSignificantMotion();
+ stopMonitoringMotionLocked();
mAnyMotionDetector.stop();
}
@@ -1271,8 +1317,8 @@ public class DeviceIdleController extends SystemService
switch (mState) {
case STATE_INACTIVE:
// We have now been inactive long enough, it is time to start looking
- // for significant motion and sleep some more while doing so.
- startMonitoringSignificantMotion();
+ // for motion and sleep some more while doing so.
+ startMonitoringMotionLocked();
scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
// Reset the upcoming idle delays.
mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
@@ -1353,17 +1399,16 @@ public class DeviceIdleController extends SystemService
}
}
- void significantMotionLocked() {
- if (DEBUG) Slog.d(TAG, "significantMotionLocked()");
- // When the sensor goes off, its trigger is automatically removed.
- mSigMotionActive = false;
+ void motionLocked() {
+ if (DEBUG) Slog.d(TAG, "motionLocked()");
+ // The motion sensor will have been disabled at this point
handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
}
void handleMotionDetectedLocked(long timeout, String type) {
// The device is not yet active, so we want to go back to the pending idle
- // state to wait again for no motion. Note that we only monitor for significant
- // motion after moving out of the inactive state, so no need to worry about that.
+ // state to wait again for no motion. Note that we only monitor for motion
+ // after moving out of the inactive state, so no need to worry about that.
if (mState != STATE_ACTIVE) {
scheduleReportActiveLocked(type, Process.myUid());
mState = STATE_ACTIVE;
@@ -1405,19 +1450,17 @@ public class DeviceIdleController extends SystemService
}
}
- void startMonitoringSignificantMotion() {
- if (DEBUG) Slog.d(TAG, "startMonitoringSignificantMotion()");
- if (mSigMotionSensor != null && !mSigMotionActive) {
- mSensorManager.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
- mSigMotionActive = true;
+ void startMonitoringMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "startMonitoringMotionLocked()");
+ if (mMotionSensor != null && !mMotionListener.active) {
+ mMotionListener.registerLocked();
}
}
- void stopMonitoringSignificantMotion() {
- if (DEBUG) Slog.d(TAG, "stopMonitoringSignificantMotion()");
- if (mSigMotionActive) {
- mSensorManager.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
- mSigMotionActive = false;
+ void stopMonitoringMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
+ if (mMotionSensor != null && mMotionListener.active) {
+ mMotionListener.unregisterLocked();
}
}
@@ -1446,7 +1489,7 @@ public class DeviceIdleController extends SystemService
void scheduleAlarmLocked(long delay, boolean idleUntil) {
if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
- if (mSigMotionSensor == null) {
+ if (mMotionSensor == null) {
// If there is no motion sensor on this device, then we won't schedule
// alarms, because we can't determine if the device is not moving. This effectively
// turns off normal execution of device idling, although it is still possible to
@@ -1929,11 +1972,11 @@ public class DeviceIdleController extends SystemService
pw.print(" mEnabled="); pw.println(mEnabled);
pw.print(" mForceIdle="); pw.println(mForceIdle);
- pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor);
+ pw.print(" mMotionSensor="); pw.println(mMotionSensor);
pw.print(" mCurDisplay="); pw.println(mCurDisplay);
pw.print(" mScreenOn="); pw.println(mScreenOn);
pw.print(" mCharging="); pw.println(mCharging);
- pw.print(" mSigMotionActive="); pw.println(mSigMotionActive);
+ pw.print(" mMotionActive="); pw.println(mMotionListener.active);
pw.print(" mSensing="); pw.print(mSensing); pw.print(" mNotMoving=");
pw.println(mNotMoving);
pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index 7c85001..f245985 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.app.ActivityManager;
+import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -263,7 +264,8 @@ public class GestureLauncherService extends SystemService {
}
if (launched) {
Slog.i(TAG, "Power button double tap gesture detected, launching camera.");
- launched = handleCameraLaunchGesture(false /* useWakelock */);
+ launched = handleCameraLaunchGesture(false /* useWakelock */,
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
if (launched) {
MetricsLogger.action(mContext, MetricsLogger.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE,
(int) doubleTapInterval);
@@ -276,7 +278,7 @@ public class GestureLauncherService extends SystemService {
/**
* @return true if camera was launched, false otherwise.
*/
- private boolean handleCameraLaunchGesture(boolean useWakelock) {
+ private boolean handleCameraLaunchGesture(boolean useWakelock, int source) {
boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
if (!userSetupComplete) {
@@ -295,7 +297,7 @@ public class GestureLauncherService extends SystemService {
}
StatusBarManagerInternal service = LocalServices.getService(
StatusBarManagerInternal.class);
- service.onCameraLaunchGestureDetected();
+ service.onCameraLaunchGestureDetected(source);
return true;
}
@@ -334,7 +336,8 @@ public class GestureLauncherService extends SystemService {
Slog.d(TAG, String.format("Received a camera launch event: " +
"values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2]));
}
- if (handleCameraLaunchGesture(true /* useWakelock */)) {
+ if (handleCameraLaunchGesture(true /* useWakelock */,
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)) {
MetricsLogger.action(mContext, MetricsLogger.ACTION_WIGGLE_CAMERA_GESTURE);
trackCameraLaunchEvent(event);
}
diff --git a/services/core/java/com/android/server/ThermalObserver.java b/services/core/java/com/android/server/ThermalObserver.java
new file mode 100644
index 0000000..aee28fb
--- /dev/null
+++ b/services/core/java/com/android/server/ThermalObserver.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.UEventObserver;
+import android.os.UserHandle;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * ThermalObserver for monitoring temperature changes.
+ */
+public class ThermalObserver extends SystemService {
+ private static final String TAG = "ThermalObserver";
+
+ private static final String CALLSTATE_UEVENT_MATCH =
+ "DEVPATH=/devices/virtual/switch/thermalstate";
+
+ private static final int MSG_THERMAL_STATE_CHANGED = 0;
+
+ private static final int SWITCH_STATE_NORMAL = 0;
+ private static final int SWITCH_STATE_WARNING = 1;
+ private static final int SWITCH_STATE_EXCEEDED = 2;
+
+ private final PowerManager mPowerManager;
+ private final PowerManager.WakeLock mWakeLock;
+
+ private final Object mLock = new Object();
+ private Integer mLastState;
+
+ private final UEventObserver mThermalWarningObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ updateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
+ }
+ };
+
+ private final Handler mHandler = new Handler(true /*async*/) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_THERMAL_STATE_CHANGED:
+ handleThermalStateChange(msg.arg1);
+ mWakeLock.release();
+ break;
+ }
+ }
+ };
+
+ public ThermalObserver(Context context) {
+ super(context);
+ mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ mThermalWarningObserver.startObserving(CALLSTATE_UEVENT_MATCH);
+ }
+
+ private void updateLocked(int state) {
+ Message message = new Message();
+ message.what = MSG_THERMAL_STATE_CHANGED;
+ message.arg1 = state;
+
+ mWakeLock.acquire();
+ mHandler.sendMessage(message);
+ }
+
+ private void handleThermalStateChange(int state) {
+ synchronized (mLock) {
+ mLastState = state;
+ Intent intent = new Intent(Intent.ACTION_THERMAL_EVENT);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+ final int thermalState;
+
+ switch (state) {
+ case SWITCH_STATE_WARNING:
+ thermalState = Intent.EXTRA_THERMAL_STATE_WARNING;
+ break;
+ case SWITCH_STATE_EXCEEDED:
+ thermalState = Intent.EXTRA_THERMAL_STATE_EXCEEDED;
+ break;
+ case SWITCH_STATE_NORMAL:
+ default:
+ thermalState = Intent.EXTRA_THERMAL_STATE_NORMAL;
+ break;
+ }
+
+ intent.putExtra(Intent.EXTRA_THERMAL_STATE, thermalState);
+
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(TAG, new BinderService());
+ }
+
+ private final class BinderService extends Binder {
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump thermal observer service from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (args == null || args.length == 0 || "-a".equals(args[0])) {
+ pw.println("Current Thermal Observer Service state:");
+ pw.println(" last state change: "
+ + (mLastState != null ? mLastState : "none"));
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7565e9d..2c6bafc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2527,11 +2527,14 @@ public class AudioService extends IAudioService.Stub {
}
/** @see AudioManager#setBluetoothScoOn(boolean) */
- public void setBluetoothScoOn(boolean on){
+ public void setBluetoothScoOn(boolean on) {
if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
return;
}
+ setBluetoothScoOnInt(on);
+ }
+ public void setBluetoothScoOnInt(boolean on) {
if (on) {
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
} else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
@@ -2892,6 +2895,8 @@ public class AudioService extends IAudioService.Stub {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
+ AudioSystem.setParameters("A2dpSuspended=false");
+ setBluetoothScoOnInt(false);
}
private void broadcastScoConnectionState(int state) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 533f425..452378f 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -837,7 +837,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (mPendingScreenOff && target != Display.STATE_OFF) {
setScreenState(Display.STATE_OFF);
mPendingScreenOff = false;
- mPowerState.dismissColorFade();
}
if (target == Display.STATE_ON) {
@@ -911,7 +910,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// A black surface is already hiding the contents of the screen.
setScreenState(Display.STATE_OFF);
mPendingScreenOff = false;
- mPowerState.dismissColorFade();
} else if (performScreenOffTransition
&& mPowerState.prepareColorFade(mContext,
mColorFadeFadesConfig ?
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index be37f52..088d96e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -544,7 +544,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
physIndex = findDisplayInfoIndexLocked(colorTransformId, modeId);
}
}
- if (physIndex > 0 && mActivePhysIndex == physIndex) {
+ if (mActivePhysIndex == physIndex) {
return;
}
SurfaceControl.setActiveConfig(getDisplayTokenLocked(), physIndex);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 80c604f..e2f9230 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4389,6 +4389,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (attrs.type == TYPE_STATUS_BAR) {
if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
mForceStatusBarFromKeyguard = true;
+ mShowingLockscreen = true;
}
if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
mForceStatusBarTransparent = true;
@@ -4409,9 +4410,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mForceStatusBar = true;
}
}
- if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
- mShowingLockscreen = true;
- }
if (attrs.type == TYPE_DREAM) {
// If the lockscreen was showing when the dream started then wait
// for the dream to draw before hiding the lockscreen.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 5d01931..25d646d 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -28,5 +28,5 @@ public interface StatusBarManagerInternal {
void showScreenPinningRequest();
void showAssistDisclosure();
void startAssist(Bundle args);
- void onCameraLaunchGestureDetected();
+ void onCameraLaunchGestureDetected(int source);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 0fb1169..e9ace29 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -178,10 +178,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
}
@Override
- public void onCameraLaunchGestureDetected() {
+ public void onCameraLaunchGestureDetected(int source) {
if (mBar != null) {
try {
- mBar.onCameraLaunchGestureDetected();
+ mBar.onCameraLaunchGestureDetected(source);
} catch (RemoteException e) {
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3effe63..08816b9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -825,6 +825,11 @@ public final class SystemServer {
if (!disableNonCoreServices) {
mSystemServiceManager.startService(DockObserver.class);
+
+ if (context.getPackageManager().hasSystemFeature
+ (PackageManager.FEATURE_WATCH)) {
+ mSystemServiceManager.startService(ThermalObserver.class);
+ }
}
try {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 9ee9cf4..e0d2ac1 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -244,8 +244,9 @@ public class DhcpClient extends BaseDhcpStateMachine {
private PendingIntent createStateMachineCommandIntent(final String cmdName, final int cmd) {
String action = DhcpClient.class.getName() + "." + mIfaceName + "." + cmdName;
- Intent intent = new Intent(action, null)
- .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ Intent intent = new Intent(action, null).addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+ Intent.FLAG_RECEIVER_FOREGROUND);
// TODO: The intent's package covers the whole of the system server, so it's pretty generic.
// Consider adding some sort of token as well.
intent.setPackage(mContext.getPackageName());
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 8779462..b07b018 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -116,6 +116,15 @@ public class TelecomManager {
"android.telecom.action.PHONE_ACCOUNT_REGISTERED";
/**
+ * The {@link android.content.Intent} action used indicate that a phone account was
+ * just unregistered.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_PHONE_ACCOUNT_UNREGISTERED =
+ "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
+
+ /**
* Activity action: Shows a dialog asking the user whether or not they want to replace the
* current default Dialer with the one specified in
* {@link #EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME}.
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
index 0b43666..8085db7 100644
--- a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/CameraActivity.java
@@ -31,5 +31,7 @@ public class CameraActivity extends Activity {
super.onCreate(savedInstanceState);
setContentView(R.layout.camera_activity);
Log.i(TAG, "Activity created");
+ Log.i(TAG, "Source: "
+ + getIntent().getStringExtra("com.android.systemui.camera_launch_source"));
}
}
diff --git a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
index 530fe00..242d3b2 100644
--- a/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
+++ b/tests/CameraPrewarmTest/src/com/google/android/test/cameraprewarm/SecureCameraActivity.java
@@ -17,6 +17,7 @@
package com.google.android.test.cameraprewarm;
import android.app.Activity;
+import android.graphics.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
@@ -31,5 +32,7 @@ public class SecureCameraActivity extends Activity {
setContentView(R.layout.camera_activity);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
Log.i(CameraActivity.TAG, "Activity created");
+ Log.i(CameraActivity.TAG, "Source: "
+ + getIntent().getStringExtra("com.android.systemui.camera_launch_source"));
}
}
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
index 3b7bf85..f4b1f2c 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
@@ -401,7 +401,7 @@ public final class BridgeResources extends Resources {
if (xml.isFile()) {
// we need to create a pull parser around the layout XML file, and then
// give that to our XmlBlockParser
- parser = ParserFactory.create(xml);
+ parser = ParserFactory.create(xml, true);
}
}
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index f1726eb..5db1bde 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -206,7 +206,7 @@ public final class BridgeInflater extends LayoutInflater {
File f = new File(value.getValue());
if (f.isFile()) {
try {
- XmlPullParser parser = ParserFactory.create(f);
+ XmlPullParser parser = ParserFactory.create(f, true);
BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
parser, bridgeContext, value.isFramework());
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 689e359..b2dc29a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -436,7 +436,7 @@ public final class BridgeContext extends Context {
// we need to create a pull parser around the layout XML file, and then
// give that to our XmlBlockParser
try {
- XmlPullParser parser = ParserFactory.create(xml);
+ XmlPullParser parser = ParserFactory.create(xml, true);
// set the resource ref to have correct view cookies
mBridgeInflater.setResourceReference(resource);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java
new file mode 100644
index 0000000..3d2a238
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutParserWrapper.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.impl;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.Nullable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A wrapper around XmlPullParser that can peek forward to inspect if the file is a data-binding
+ * layout and some parts need to be stripped.
+ */
+public class LayoutParserWrapper implements XmlPullParser {
+
+ // Data binding constants.
+ private static final String TAG_LAYOUT = "layout";
+ private static final String TAG_DATA = "data";
+ private static final String DEFAULT = "default=";
+
+ private final XmlPullParser mDelegate;
+
+ // Storage for peeked values.
+ private boolean mPeeked;
+ private int mEventType;
+ private int mDepth;
+ private int mNext;
+ private List<Attribute> mAttributes;
+ private String mText;
+ private String mName;
+
+ // Used to end the document before the actual parser ends.
+ private int mFinalDepth = -1;
+ private boolean mEndNow;
+
+ public LayoutParserWrapper(XmlPullParser delegate) {
+ mDelegate = delegate;
+ }
+
+ public LayoutParserWrapper peekTillLayoutStart() throws IOException, XmlPullParserException {
+ final int STATE_LAYOUT_NOT_STARTED = 0; // <layout> tag not encountered yet.
+ final int STATE_ROOT_NOT_STARTED = 1; // the main view root not found yet.
+ final int STATE_INSIDE_DATA = 2; // START_TAG for <data> found, but not END_TAG.
+
+ int state = STATE_LAYOUT_NOT_STARTED;
+ int dataDepth = -1; // depth of the <data> tag. Should be two.
+ while (true) {
+ int peekNext = peekNext();
+ switch (peekNext) {
+ case START_TAG:
+ if (state == STATE_LAYOUT_NOT_STARTED) {
+ if (mName.equals(TAG_LAYOUT)) {
+ state = STATE_ROOT_NOT_STARTED;
+ } else {
+ return this; // no layout tag in the file.
+ }
+ } else if (state == STATE_ROOT_NOT_STARTED) {
+ if (mName.equals(TAG_DATA)) {
+ state = STATE_INSIDE_DATA;
+ dataDepth = mDepth;
+ } else {
+ mFinalDepth = mDepth;
+ return this;
+ }
+ }
+ break;
+ case END_TAG:
+ if (state == STATE_INSIDE_DATA) {
+ if (mDepth <= dataDepth) {
+ state = STATE_ROOT_NOT_STARTED;
+ }
+ }
+ break;
+ case END_DOCUMENT:
+ // No layout start found.
+ return this;
+ }
+ // consume the peeked tag.
+ next();
+ }
+ }
+
+ private int peekNext() throws IOException, XmlPullParserException {
+ if (mPeeked) {
+ return mNext;
+ }
+ mEventType = mDelegate.getEventType();
+ mNext = mDelegate.next();
+ if (mEventType == START_TAG) {
+ int count = mDelegate.getAttributeCount();
+ mAttributes = count > 0 ? new ArrayList<Attribute>(count) :
+ Collections.<Attribute>emptyList();
+ for (int i = 0; i < count; i++) {
+ mAttributes.add(new Attribute(mDelegate.getAttributeNamespace(i),
+ mDelegate.getAttributeName(i), mDelegate.getAttributeValue(i)));
+ }
+ }
+ mDepth = mDelegate.getDepth();
+ mText = mDelegate.getText();
+ mName = mDelegate.getName();
+ mPeeked = true;
+ return mNext;
+ }
+
+ private void reset() {
+ mAttributes = null;
+ mText = null;
+ mName = null;
+ mPeeked = false;
+ }
+
+ @Override
+ public int next() throws XmlPullParserException, IOException {
+ int returnValue;
+ int depth;
+ if (mPeeked) {
+ returnValue = mNext;
+ depth = mDepth;
+ reset();
+ } else if (mEndNow) {
+ return END_DOCUMENT;
+ } else {
+ returnValue = mDelegate.next();
+ depth = getDepth();
+ }
+ if (returnValue == END_TAG && depth <= mFinalDepth) {
+ mEndNow = true;
+ }
+ return returnValue;
+ }
+
+ @Override
+ public int getEventType() throws XmlPullParserException {
+ return mPeeked ? mEventType : mDelegate.getEventType();
+ }
+
+ @Override
+ public int getDepth() {
+ return mPeeked ? mDepth : mDelegate.getDepth();
+ }
+
+ @Override
+ public String getName() {
+ return mPeeked ? mName : mDelegate.getName();
+ }
+
+ @Override
+ public String getText() {
+ return mPeeked ? mText : mDelegate.getText();
+ }
+
+ @Override
+ public String getAttributeValue(@Nullable String namespace, String name) {
+ String returnValue = null;
+ if (mPeeked) {
+ if (mAttributes == null) {
+ if (mEventType != START_TAG) {
+ throw new IndexOutOfBoundsException("getAttributeValue() called when not at " +
+ "START_TAG.");
+ } else {
+ return null;
+ }
+ } else {
+ for (Attribute attribute : mAttributes) {
+ //noinspection StringEquality for nullness check.
+ if (attribute.name.equals(name) && (attribute.namespace == namespace ||
+ attribute.namespace != null && attribute.namespace.equals(namespace))) {
+ returnValue = attribute.value;
+ break;
+ }
+ }
+ }
+ } else {
+ returnValue = mDelegate.getAttributeValue(namespace, name);
+ }
+ // Check if the value is bound via data-binding, if yes get the default value.
+ if (returnValue != null && mFinalDepth >= 0 && returnValue.startsWith("@{")) {
+ // TODO: Improve the detection of default keyword.
+ int i = returnValue.lastIndexOf(DEFAULT);
+ return i > 0 ? returnValue.substring(i + DEFAULT.length(), returnValue.length() - 1)
+ : null;
+ }
+ return returnValue;
+ }
+
+ private static class Attribute {
+ @Nullable
+ public final String namespace;
+ public final String name;
+ public final String value;
+
+ public Attribute(@Nullable String namespace, String name, String value) {
+ this.namespace = namespace;
+ this.name = name;
+ this.value = value;
+ }
+ }
+
+ // Not affected by peeking.
+
+ @Override
+ public void setFeature(String s, boolean b) throws XmlPullParserException {
+ mDelegate.setFeature(s, b);
+ }
+
+ @Override
+ public void setProperty(String s, Object o) throws XmlPullParserException {
+ mDelegate.setProperty(s, o);
+ }
+
+ @Override
+ public void setInput(InputStream inputStream, String s) throws XmlPullParserException {
+ mDelegate.setInput(inputStream, s);
+ }
+
+ @Override
+ public void setInput(Reader reader) throws XmlPullParserException {
+ mDelegate.setInput(reader);
+ }
+
+ @Override
+ public String getInputEncoding() {
+ return mDelegate.getInputEncoding();
+ }
+
+ @Override
+ public String getNamespace(String s) {
+ return mDelegate.getNamespace(s);
+ }
+
+ @Override
+ public String getPositionDescription() {
+ return mDelegate.getPositionDescription();
+ }
+
+ @Override
+ public int getLineNumber() {
+ return mDelegate.getLineNumber();
+ }
+
+ @Override
+ public String getNamespace() {
+ return mDelegate.getNamespace();
+ }
+
+ @Override
+ public int getColumnNumber() {
+ return mDelegate.getColumnNumber();
+ }
+
+ // -- We don't care much about the methods that follow.
+
+ @Override
+ public void require(int i, String s, String s1) throws XmlPullParserException, IOException {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public boolean getFeature(String s) {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public void defineEntityReplacementText(String s, String s1) throws XmlPullParserException {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public Object getProperty(String s) {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public int nextToken() throws XmlPullParserException, IOException {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public int getNamespaceCount(int i) throws XmlPullParserException {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public String getNamespacePrefix(int i) throws XmlPullParserException {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public String getNamespaceUri(int i) throws XmlPullParserException {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public boolean isWhitespace() throws XmlPullParserException {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public char[] getTextCharacters(int[] ints) {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public String getPrefix() {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public boolean isEmptyElementTag() throws XmlPullParserException {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public int getAttributeCount() {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public String getAttributeNamespace(int i) {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public String getAttributeName(int i) {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public String getAttributePrefix(int i) {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public String getAttributeType(int i) {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public boolean isAttributeDefault(int i) {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public String getAttributeValue(int i) {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public String nextText() throws XmlPullParserException, IOException {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+
+ @Override
+ public int nextTag() throws XmlPullParserException, IOException {
+ throw new UnsupportedOperationException("Only few parser methods are supported.");
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
index 6e67f59..e273b2c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ParserFactory.java
@@ -53,24 +53,35 @@ public class ParserFactory {
@NonNull
public static XmlPullParser create(@NonNull File f)
throws XmlPullParserException, FileNotFoundException {
- InputStream stream = new FileInputStream(f);
- return create(stream, f.getName(), f.length());
+ return create(f, false);
}
+ public static XmlPullParser create(@NonNull File f, boolean isLayout)
+ throws XmlPullParserException, FileNotFoundException {
+ InputStream stream = new FileInputStream(f);
+ return create(stream, f.getName(), f.length(), isLayout);
+ }
@NonNull
public static XmlPullParser create(@NonNull InputStream stream, @Nullable String name)
throws XmlPullParserException {
- return create(stream, name, -1);
+ return create(stream, name, -1, false);
}
@NonNull
private static XmlPullParser create(@NonNull InputStream stream, @Nullable String name,
- long size) throws XmlPullParserException {
+ long size, boolean isLayout) throws XmlPullParserException {
XmlPullParser parser = instantiateParser(name);
stream = readAndClose(stream, name, size);
parser.setInput(stream, ENCODING);
+ if (isLayout) {
+ try {
+ return new LayoutParserWrapper(parser).peekTillLayoutStart();
+ } catch (IOException e) {
+ throw new XmlPullParserException(null, parser, e);
+ }
+ }
return parser;
}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java
new file mode 100644
index 0000000..2c33862
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/impl/LayoutParserWrapperTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.impl;
+
+import org.junit.Test;
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.StringReader;
+
+import static com.android.SdkConstants.NS_RESOURCES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+
+public class LayoutParserWrapperTest {
+ @Test
+ @SuppressWarnings("StatementWithEmptyBody") // some for loops need to be empty statements.
+ public void testDataBindingLayout() throws Exception {
+ LayoutParserWrapper parser = getParserFromString(sDataBindingLayout);
+ parser.peekTillLayoutStart();
+ assertEquals("Expected START_TAG", START_TAG, parser.next());
+ assertEquals("RelativeLayout", parser.getName());
+ for (int next = parser.next(); next != START_TAG && next != END_DOCUMENT;
+ next = parser.next());
+ assertEquals("Expected START_TAG", START_TAG, parser.getEventType());
+ assertEquals("TextView", parser.getName());
+ assertEquals("layout_width incorrect for first text view.", "wrap_content",
+ parser.getAttributeValue(NS_RESOURCES, "layout_width"));
+ // Ensure that data-binding part is stripped.
+ assertEquals("Bound attribute android:text incorrect", "World",
+ parser.getAttributeValue(NS_RESOURCES, "text"));
+ assertEquals("resource attribute 'id' for first text view incorrect.", "@+id/first",
+ parser.getAttributeValue(NS_RESOURCES, "id"));
+ for (int next = parser.next();
+ (next != END_TAG || !"RelativeLayout".equals(parser.getName())) && next != END_DOCUMENT;
+ next = parser.next());
+ assertNotSame("Unexpected end of document", END_DOCUMENT, parser.getEventType());
+ assertEquals("Document didn't end when expected.", END_DOCUMENT, parser.next());
+ }
+
+ @Test
+ @SuppressWarnings("StatementWithEmptyBody")
+ public void testNonDataBindingLayout() throws Exception {
+ LayoutParserWrapper parser = getParserFromString(sNonDataBindingLayout);
+ parser.peekTillLayoutStart();
+ assertEquals("Expected START_TAG", START_TAG, parser.next());
+ assertEquals("RelativeLayout", parser.getName());
+ for (int next = parser.next(); next != START_TAG && next != END_DOCUMENT;
+ next = parser.next());
+ assertEquals("Expected START_TAG", START_TAG, parser.getEventType());
+ assertEquals("TextView", parser.getName());
+ assertEquals("layout_width incorrect for first text view.", "wrap_content",
+ parser.getAttributeValue(NS_RESOURCES, "layout_width"));
+ // Ensure that value isn't modified.
+ assertEquals("Bound attribute android:text incorrect", "@{user.firstName,default=World}",
+ parser.getAttributeValue(NS_RESOURCES, "text"));
+ assertEquals("resource attribute 'id' for first text view incorrect.", "@+id/first",
+ parser.getAttributeValue(NS_RESOURCES, "id"));
+ for (int next = parser.next();
+ (next != END_TAG || !"RelativeLayout".equals(parser.getName())) && next != END_DOCUMENT;
+ next = parser.next());
+ assertNotSame("Unexpected end of document", END_DOCUMENT, parser.getEventType());
+ assertEquals("Document didn't end when expected.", END_DOCUMENT, parser.next());
+ }
+
+ private static LayoutParserWrapper getParserFromString(String layoutContent) throws
+ XmlPullParserException {
+ XmlPullParser parser = new KXmlParser();
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ parser.setInput(new StringReader(layoutContent));
+ return new LayoutParserWrapper(parser);
+ }
+
+ private static final String sDataBindingLayout =
+ //language=XML
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
+ "<layout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n" +
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n" +
+ " tools:context=\".MainActivity\"\n" +
+ " tools:showIn=\"@layout/activity_main\">\n" +
+ "\n" +
+ " <data>\n" +
+ "\n" +
+ " <variable\n" +
+ " name=\"user\"\n" +
+ " type=\"com.example.User\" />\n" +
+ " <variable\n" +
+ " name=\"activity\"\n" +
+ " type=\"com.example.MainActivity\" />\n" +
+ " </data>\n" +
+ "\n" +
+ " <RelativeLayout\n" +
+ " android:layout_width=\"match_parent\"\n" +
+ " android:layout_height=\"match_parent\"\n" +
+ " android:paddingBottom=\"@dimen/activity_vertical_margin\"\n" +
+ " android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" +
+ " android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" +
+ " android:paddingTop=\"@dimen/activity_vertical_margin\"\n" +
+ " app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n" +
+ " >\n" +
+ "\n" +
+ " <TextView\n" +
+ " android:id=\"@+id/first\"\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_alignParentStart=\"true\"\n" +
+ " android:layout_alignParentLeft=\"true\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:text=\"@{user.firstName,default=World}\" />\n" +
+ "\n" +
+ " <TextView\n" +
+ " android:id=\"@+id/last\"\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:layout_toEndOf=\"@id/first\"\n" +
+ " android:layout_toRightOf=\"@id/first\"\n" +
+ " android:text=\"@{user.lastName,default=Hello}\" />\n" +
+ "\n" +
+ " <Button\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:layout_below=\"@id/last\"\n" +
+ " android:text=\"Submit\"\n" +
+ " android:onClick=\"@{activity.onClick}\"/>\n" +
+ " </RelativeLayout>\n" +
+ "</layout>";
+
+ private static final String sNonDataBindingLayout =
+ //language=XML
+ "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n" +
+ " android:layout_width=\"match_parent\"\n" +
+ " android:layout_height=\"match_parent\"\n" +
+ " android:paddingBottom=\"@dimen/activity_vertical_margin\"\n" +
+ " android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n" +
+ " android:paddingRight=\"@dimen/activity_horizontal_margin\"\n" +
+ " android:paddingTop=\"@dimen/activity_vertical_margin\"\n" +
+ " app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n" +
+ ">\n" +
+ "\n" +
+ " <TextView\n" +
+ " android:id=\"@+id/first\"\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_alignParentStart=\"true\"\n" +
+ " android:layout_alignParentLeft=\"true\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:text=\"@{user.firstName,default=World}\" />\n" +
+ "\n" +
+ " <TextView\n" +
+ " android:id=\"@+id/last\"\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:layout_toEndOf=\"@id/first\"\n" +
+ " android:layout_toRightOf=\"@id/first\"\n" +
+ " android:text=\"@{user.lastName,default=Hello}\" />\n" +
+ "\n" +
+ " <Button\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:layout_below=\"@id/last\"\n" +
+ " android:text=\"Submit\"\n" +
+ " android:onClick=\"@{activity.onClick}\"/>\n" +
+ "</RelativeLayout>";
+}