summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--api/current.txt35
-rw-r--r--core/java/android/app/Activity.java2
-rw-r--r--core/java/android/app/ContextImpl.java9
-rw-r--r--core/java/android/app/Notification.java169
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java44
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/content/Context.java14
-rw-r--r--core/java/android/provider/Settings.java18
-rw-r--r--core/java/android/widget/ShareActionProvider.java9
-rw-r--r--core/java/com/android/internal/app/PlatLogoActivity.java230
-rw-r--r--core/java/com/android/internal/util/NotificationColorUtil.java (renamed from core/java/com/android/internal/util/LegacyNotificationUtil.java)14
-rw-r--r--core/java/com/android/internal/util/Protocol.java2
-rw-r--r--core/res/AndroidManifest.xml3
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_adb_am.pngbin213 -> 0 bytes
-rw-r--r--core/res/res/drawable-ldpi/stat_sys_adb_am.pngbin414 -> 0 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_adb_am.pngbin161 -> 0 bytes
-rw-r--r--core/res/res/drawable-nodpi/platlogo.pngbin39802 -> 0 bytes
-rw-r--r--core/res/res/drawable-nodpi/platlogo.xml39
-rw-r--r--core/res/res/drawable-nodpi/stat_sys_adb.xml30
-rw-r--r--core/res/res/drawable-xhdpi/stat_sys_adb_am.pngbin255 -> 0 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/stat_sys_adb_am.pngbin327 -> 0 bytes
-rw-r--r--core/res/res/drawable/notification_icon_legacy_bg.xml2
-rw-r--r--core/res/res/values/colors.xml2
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml2
-rw-r--r--packages/SystemUI/res/values/ids.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java152
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java192
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java110
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java7
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java12
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java3
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityStack.java26
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java42
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java8
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java5
-rw-r--r--services/core/java/com/android/server/notification/NotificationUsageStats.java227
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java8
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java69
-rw-r--r--services/java/com/android/server/SystemServer.java25
-rw-r--r--wifi/java/android/net/wifi/IWifiScanner.aidl (renamed from core/res/res/drawable/stat_sys_adb.xml)22
-rw-r--r--wifi/java/android/net/wifi/ScanResult.java15
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java255
-rw-r--r--wifi/java/android/net/wifi/WifiScanner.java584
59 files changed, 2219 insertions, 609 deletions
diff --git a/Android.mk b/Android.mk
index beee726..99d73fa 100644
--- a/Android.mk
+++ b/Android.mk
@@ -320,6 +320,7 @@ LOCAL_SRC_FILES += \
wifi/java/android/net/wifi/IWifiManager.aidl \
wifi/java/android/net/wifi/hotspot/IWifiHotspotManager.aidl \
wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
+ wifi/java/android/net/wifi/IWifiScanner.aidl \
packages/services/PacProcessor/com/android/net/IProxyService.aidl \
packages/services/Proxy/com/android/net/IProxyCallback.aidl \
packages/services/Proxy/com/android/net/IProxyPortListener.aidl \
diff --git a/api/current.txt b/api/current.txt
index 79f8e10..302e77e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4438,6 +4438,7 @@ package android.app {
field public static final java.lang.String CATEGORY_STATUS = "status";
field public static final java.lang.String CATEGORY_SYSTEM = "sys";
field public static final java.lang.String CATEGORY_TRANSPORT = "transport";
+ field public static final int COLOR_DEFAULT = 0; // 0x0
field public static final android.os.Parcelable.Creator CREATOR;
field public static final int DEFAULT_ALL = -1; // 0xffffffff
field public static final int DEFAULT_LIGHTS = 4; // 0x4
@@ -4483,6 +4484,7 @@ package android.app {
field public int audioStreamType;
field public android.widget.RemoteViews bigContentView;
field public java.lang.String category;
+ field public int color;
field public android.app.PendingIntent contentIntent;
field public android.widget.RemoteViews contentView;
field public int defaults;
@@ -4545,6 +4547,7 @@ package android.app {
method public deprecated android.app.Notification getNotification();
method public android.app.Notification.Builder setAutoCancel(boolean);
method public android.app.Notification.Builder setCategory(java.lang.String);
+ method public android.app.Notification.Builder setColor(int);
method public android.app.Notification.Builder setContent(android.widget.RemoteViews);
method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
@@ -5027,6 +5030,7 @@ package android.app.admin {
method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
method public void enableSystemApp(android.content.ComponentName, java.lang.String);
method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
+ method public java.lang.String[] getAccountTypesWithManagementDisabled();
method public java.util.List<android.content.ComponentName> getActiveAdmins();
method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
method public boolean getCameraDisabled(android.content.ComponentName);
@@ -5056,6 +5060,7 @@ package android.app.admin {
method public void lockNow();
method public void removeActiveAdmin(android.content.ComponentName);
method public boolean resetPassword(java.lang.String, int);
+ method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
@@ -39827,11 +39832,13 @@ package java.net {
method public java.lang.String getValue();
method public int getVersion();
method public boolean hasExpired();
+ method public boolean isHttpOnly();
method public static java.util.List<java.net.HttpCookie> parse(java.lang.String);
method public void setComment(java.lang.String);
method public void setCommentURL(java.lang.String);
method public void setDiscard(boolean);
method public void setDomain(java.lang.String);
+ method public void setHttpOnly(boolean);
method public void setMaxAge(long);
method public void setPath(java.lang.String);
method public void setPortlist(java.lang.String);
@@ -40784,7 +40791,7 @@ package java.nio.channels {
ctor public ConnectionPendingException();
}
- public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.MulticastChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
method public java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
@@ -40792,6 +40799,8 @@ package java.nio.channels {
method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
method public abstract boolean isConnected();
+ method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
+ method public java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
method public static java.nio.channels.DatagramChannel open() throws java.io.IOException;
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
@@ -40839,6 +40848,7 @@ package java.nio.channels {
public abstract class FileLock implements java.lang.AutoCloseable {
ctor protected FileLock(java.nio.channels.FileChannel, long, long, boolean);
+ method public java.nio.channels.Channel acquiredBy();
method public final java.nio.channels.FileChannel channel();
method public final void close() throws java.io.IOException;
method public final boolean isShared();
@@ -40871,6 +40881,24 @@ package java.nio.channels {
method public abstract void close() throws java.io.IOException;
}
+ public abstract class MembershipKey {
+ ctor protected MembershipKey();
+ method public abstract java.nio.channels.MembershipKey block(java.net.InetAddress) throws java.io.IOException;
+ method public abstract java.nio.channels.MulticastChannel channel();
+ method public abstract void drop();
+ method public abstract java.net.InetAddress group();
+ method public abstract boolean isValid();
+ method public abstract java.net.NetworkInterface networkInterface();
+ method public abstract java.net.InetAddress sourceAddress();
+ method public abstract java.nio.channels.MembershipKey unblock(java.net.InetAddress);
+ }
+
+ public abstract interface MulticastChannel implements java.nio.channels.NetworkChannel {
+ method public abstract void close() throws java.io.IOException;
+ method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface) throws java.io.IOException;
+ method public abstract java.nio.channels.MembershipKey join(java.net.InetAddress, java.net.NetworkInterface, java.net.InetAddress) throws java.io.IOException;
+ }
+
public abstract interface NetworkChannel implements java.lang.AutoCloseable java.nio.channels.Channel java.io.Closeable {
method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
@@ -44976,6 +45004,7 @@ package java.util {
method public java.lang.String getDisplayName(java.util.Locale);
method public static java.util.Currency getInstance(java.lang.String);
method public static java.util.Currency getInstance(java.util.Locale);
+ method public int getNumericCode();
method public java.lang.String getSymbol();
method public java.lang.String getSymbol(java.util.Locale);
}
@@ -48058,8 +48087,10 @@ package java.util.zip {
public class ZipFile implements java.io.Closeable {
ctor public ZipFile(java.io.File) throws java.io.IOException, java.util.zip.ZipException;
+ ctor public ZipFile(java.io.File, java.nio.charset.Charset) throws java.io.IOException, java.util.zip.ZipException;
ctor public ZipFile(java.lang.String) throws java.io.IOException;
ctor public ZipFile(java.io.File, int) throws java.io.IOException;
+ ctor public ZipFile(java.io.File, int, java.nio.charset.Charset) throws java.io.IOException;
method public void close() throws java.io.IOException;
method public java.util.Enumeration<? extends java.util.zip.ZipEntry> entries();
method public java.lang.String getComment();
@@ -48113,6 +48144,7 @@ package java.util.zip {
public class ZipInputStream extends java.util.zip.InflaterInputStream {
ctor public ZipInputStream(java.io.InputStream);
+ ctor public ZipInputStream(java.io.InputStream, java.nio.charset.Charset);
method public void closeEntry() throws java.io.IOException;
method protected java.util.zip.ZipEntry createZipEntry(java.lang.String);
method public java.util.zip.ZipEntry getNextEntry() throws java.io.IOException;
@@ -48160,6 +48192,7 @@ package java.util.zip {
public class ZipOutputStream extends java.util.zip.DeflaterOutputStream {
ctor public ZipOutputStream(java.io.OutputStream);
+ ctor public ZipOutputStream(java.io.OutputStream, java.nio.charset.Charset);
method public void closeEntry() throws java.io.IOException;
method public void putNextEntry(java.util.zip.ZipEntry) throws java.io.IOException;
method public void setComment(java.lang.String);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 4df486a..36f9f4b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5698,7 +5698,7 @@ public class Activity extends ContextThemeWrapper
* or reaching the home screen.
*
* Lock task mode will only start if the activity has been whitelisted by the
- * Device Owner through {@link DevicePolicyManager#setLockTaskComponents}.
+ * Device Owner through DevicePolicyManager#setLockTaskComponents.
*/
public void startLockTask() {
try {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index c621696..5ed5030 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -16,6 +16,8 @@
package android.app;
+import android.net.wifi.IWifiScanner;
+import android.net.wifi.WifiScanner;
import android.os.Build;
import com.android.internal.policy.PolicyManager;
@@ -589,6 +591,13 @@ class ContextImpl extends Context {
return new WifiP2pManager(service);
}});
+ registerService(WIFI_SCANNING_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(WIFI_SCANNING_SERVICE);
+ IWifiScanner service = IWifiScanner.Stub.asInterface(b);
+ return new WifiScanner(ctx.getOuterContext(), service);
+ }});
+
registerService(WINDOW_SERVICE, new ServiceFetcher() {
Display mDefaultDisplay;
public Object getService(ContextImpl ctx) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 25a1493..bba6caf 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -17,7 +17,7 @@
package android.app;
import com.android.internal.R;
-import com.android.internal.util.LegacyNotificationUtil;
+import com.android.internal.util.NotificationColorUtil;
import android.annotation.IntDef;
import android.content.Context;
@@ -28,6 +28,7 @@ import android.graphics.PorterDuff;
import android.media.AudioManager;
import android.net.Uri;
import android.os.BadParcelableException;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -420,6 +421,21 @@ public class Notification implements Parcelable
@Priority
public int priority;
+ /**
+ * Accent color (an ARGB integer like the constants in {@link android.graphics.Color})
+ * to be applied by the standard Style templates when presenting this notification.
+ *
+ * The current template design constructs a colorful header image by overlaying the
+ * {@link #icon} image (stenciled in white) atop a field of this color. Alpha components are
+ * ignored.
+ */
+ public int color = COLOR_DEFAULT;
+
+ /**
+ * Special value of {@link #color} telling the system not to decorate this notification with
+ * any special color but instead use default colors when presenting this notification.
+ */
+ public static final int COLOR_DEFAULT = 0; // AKA Color.TRANSPARENT
/**
* Sphere of visibility of this notification, which affects how and when the SystemUI reveals
@@ -877,6 +893,8 @@ public class Notification implements Parcelable
if (parcel.readInt() != 0) {
publicVersion = Notification.CREATOR.createFromParcel(parcel);
}
+
+ color = parcel.readInt();
}
@Override
@@ -968,6 +986,8 @@ public class Notification implements Parcelable
this.publicVersion.cloneInto(that.publicVersion, heavy);
}
+ that.color = this.color;
+
if (!heavy) {
that.lightenPayload(); // will clean out extras
}
@@ -1110,6 +1130,8 @@ public class Notification implements Parcelable
} else {
parcel.writeInt(0);
}
+
+ parcel.writeInt(color);
}
/**
@@ -1218,6 +1240,7 @@ public class Notification implements Parcelable
sb.append(Integer.toHexString(this.defaults));
sb.append(" flags=0x");
sb.append(Integer.toHexString(this.flags));
+ sb.append(String.format(" color=0x%08x", this.color));
sb.append(" category="); sb.append(this.category);
if (actions != null) {
sb.append(" ");
@@ -1309,9 +1332,10 @@ public class Notification implements Parcelable
private boolean mShowWhen = true;
private int mVisibility = VISIBILITY_PRIVATE;
private Notification mPublicVersion = null;
- private boolean mQuantumTheme;
- private final LegacyNotificationUtil mLegacyNotificationUtil;
+ private final NotificationColorUtil mColorUtil;
private ArrayList<String> mPeople;
+ private boolean mPreQuantum;
+ private int mColor = COLOR_DEFAULT;
/**
* Constructs a new Builder with the defaults:
@@ -1341,12 +1365,8 @@ public class Notification implements Parcelable
mPriority = PRIORITY_DEFAULT;
mPeople = new ArrayList<String>();
- // TODO: Decide on targetSdk from calling app whether to use quantum theme.
- mQuantumTheme = true;
-
- // TODO: Decide on targetSdk from calling app whether to instantiate the processor at
- // all.
- mLegacyNotificationUtil = LegacyNotificationUtil.getInstance();
+ mPreQuantum = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L;
+ mColorUtil = NotificationColorUtil.getInstance();
}
/**
@@ -1853,29 +1873,38 @@ public class Notification implements Parcelable
}
}
+ /**
+ * Sets {@link Notification#color}.
+ *
+ * @param argb The accent color to use
+ *
+ * @return The same Builder.
+ */
+ public Builder setColor(int argb) {
+ mColor = argb;
+ return this;
+ }
+
private RemoteViews applyStandardTemplate(int resId, boolean fitIn1U) {
RemoteViews contentView = new RemoteViews(mContext.getPackageName(), resId);
boolean showLine3 = false;
boolean showLine2 = false;
int smallIconImageViewId = R.id.icon;
- if (!mQuantumTheme && mPriority < PRIORITY_LOW) {
- contentView.setInt(R.id.icon,
- "setBackgroundResource", R.drawable.notification_template_icon_low_bg);
- contentView.setInt(R.id.status_bar_latest_event_content,
- "setBackgroundResource", R.drawable.notification_bg_low);
+ if (mPriority < PRIORITY_LOW) {
+ // TODO: Low priority presentation
}
if (mLargeIcon != null) {
contentView.setImageViewBitmap(R.id.icon, mLargeIcon);
- processLegacyLargeIcon(mLargeIcon, contentView);
+ processLargeIcon(mLargeIcon, contentView);
smallIconImageViewId = R.id.right_icon;
}
if (mSmallIcon != 0) {
contentView.setImageViewResource(smallIconImageViewId, mSmallIcon);
contentView.setViewVisibility(smallIconImageViewId, View.VISIBLE);
if (mLargeIcon != null) {
- processLegacySmallIcon(mSmallIcon, smallIconImageViewId, contentView);
+ processSmallRightIcon(mSmallIcon, smallIconImageViewId, contentView);
} else {
- processLegacyLargeIcon(mSmallIcon, contentView);
+ processSmallIconAsLarge(mSmallIcon, contentView);
}
} else {
@@ -2035,12 +2064,12 @@ public class Notification implements Parcelable
* doesn't create quantum notifications by itself) app.
*/
private boolean isLegacy() {
- return mLegacyNotificationUtil != null;
+ return mColorUtil != null;
}
private void processLegacyAction(Action action, RemoteViews button) {
if (isLegacy()) {
- if (mLegacyNotificationUtil.isGrayscale(mContext, action.icon)) {
+ if (mColorUtil.isGrayscale(mContext, action.icon)) {
button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
mContext.getResources().getColor(
R.color.notification_action_legacy_color_filter),
@@ -2051,45 +2080,68 @@ public class Notification implements Parcelable
private CharSequence processLegacyText(CharSequence charSequence) {
if (isLegacy()) {
- return mLegacyNotificationUtil.invertCharSequenceColors(charSequence);
+ return mColorUtil.invertCharSequenceColors(charSequence);
} else {
return charSequence;
}
}
- private void processLegacyLargeIcon(int largeIconId, RemoteViews contentView) {
- if (isLegacy()) {
- processLegacyLargeIcon(
- mLegacyNotificationUtil.isGrayscale(mContext, largeIconId),
- contentView);
+ /**
+ * Apply any necessary background to smallIcons being used in the largeIcon spot.
+ */
+ private void processSmallIconAsLarge(int largeIconId, RemoteViews contentView) {
+ if (!isLegacy() || mColorUtil.isGrayscale(mContext, largeIconId)) {
+ applyLargeIconBackground(contentView);
}
}
- private void processLegacyLargeIcon(Bitmap largeIcon, RemoteViews contentView) {
- if (isLegacy()) {
- processLegacyLargeIcon(
- mLegacyNotificationUtil.isGrayscale(largeIcon),
- contentView);
+ /**
+ * Apply any necessary background to a largeIcon if it's a fake smallIcon (that is,
+ * if it's grayscale).
+ */
+ // TODO: also check bounds, transparency, that sort of thing.
+ private void processLargeIcon(Bitmap largeIcon, RemoteViews contentView) {
+ if (!isLegacy() || mColorUtil.isGrayscale(largeIcon)) {
+ applyLargeIconBackground(contentView);
}
}
- private void processLegacyLargeIcon(boolean isGrayscale, RemoteViews contentView) {
- if (isLegacy() && isGrayscale) {
- contentView.setInt(R.id.icon, "setBackgroundResource",
- R.drawable.notification_icon_legacy_bg_inset);
- }
+ /**
+ * Add a colored circle behind the largeIcon slot.
+ */
+ private void applyLargeIconBackground(RemoteViews contentView) {
+ contentView.setInt(R.id.icon, "setBackgroundResource",
+ R.drawable.notification_icon_legacy_bg_inset);
+
+ contentView.setDrawableParameters(
+ R.id.icon,
+ true,
+ -1,
+ mColor,
+ PorterDuff.Mode.SRC_ATOP,
+ -1);
}
- private void processLegacySmallIcon(int smallIconDrawableId, int smallIconImageViewId,
+ /**
+ * Recolor small icons when used in the R.id.right_icon slot.
+ */
+ private void processSmallRightIcon(int smallIconDrawableId, int smallIconImageViewId,
RemoteViews contentView) {
- if (isLegacy()) {
- if (mLegacyNotificationUtil.isGrayscale(mContext, smallIconDrawableId)) {
- contentView.setDrawableParameters(smallIconImageViewId, false, -1,
- mContext.getResources().getColor(
- R.color.notification_action_legacy_color_filter),
- PorterDuff.Mode.MULTIPLY, -1);
- }
+ if (!isLegacy() || mColorUtil.isGrayscale(mContext, smallIconDrawableId)) {
+ contentView.setDrawableParameters(smallIconImageViewId, false, -1,
+ mContext.getResources().getColor(
+ R.color.notification_action_legacy_color_filter),
+ PorterDuff.Mode.MULTIPLY, -1);
+ }
+ }
+
+ private int resolveColor() {
+ if (mColor == COLOR_DEFAULT) {
+ mColor = mContext.getResources().getColor(R.color.notification_icon_bg_color);
+ } else {
+ mColor |= 0xFF000000; // no alpha for custom colors
}
+ return mColor;
}
/**
@@ -2102,6 +2154,9 @@ public class Notification implements Parcelable
n.icon = mSmallIcon;
n.iconLevel = mSmallIconLevel;
n.number = mNumber;
+
+ n.color = resolveColor();
+
n.contentView = makeContentView();
n.contentIntent = mContentIntent;
n.deleteIntent = mDeleteIntent;
@@ -2207,45 +2262,31 @@ public class Notification implements Parcelable
private int getBaseLayoutResource() {
- return mQuantumTheme
- ? R.layout.notification_template_quantum_base
- : R.layout.notification_template_base;
+ return R.layout.notification_template_quantum_base;
}
private int getBigBaseLayoutResource() {
- return mQuantumTheme
- ? R.layout.notification_template_quantum_big_base
- : R.layout.notification_template_big_base;
+ return R.layout.notification_template_quantum_big_base;
}
private int getBigPictureLayoutResource() {
- return mQuantumTheme
- ? R.layout.notification_template_quantum_big_picture
- : R.layout.notification_template_big_picture;
+ return R.layout.notification_template_quantum_big_picture;
}
private int getBigTextLayoutResource() {
- return mQuantumTheme
- ? R.layout.notification_template_quantum_big_text
- : R.layout.notification_template_big_text;
+ return R.layout.notification_template_quantum_big_text;
}
private int getInboxLayoutResource() {
- return mQuantumTheme
- ? R.layout.notification_template_quantum_inbox
- : R.layout.notification_template_inbox;
+ return R.layout.notification_template_quantum_inbox;
}
private int getActionLayoutResource() {
- return mQuantumTheme
- ? R.layout.notification_quantum_action
- : R.layout.notification_action;
+ return R.layout.notification_quantum_action;
}
private int getActionTombstoneLayoutResource() {
- return mQuantumTheme
- ? R.layout.notification_quantum_action_tombstone
- : R.layout.notification_action_tombstone;
+ return R.layout.notification_quantum_action_tombstone;
}
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 209c536..61ff60a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2062,6 +2062,28 @@ public class DevicePolicyManager {
}
/**
+ * Called by a profile owner to disable account management for a specific type of account.
+ *
+ * <p>The calling device admin must be a profile owner. If it is not, a
+ * security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param accountType For which account management is disabled or enabled.
+ * @param disabled The boolean indicating that account management will be disabled (true) or
+ * enabled (false).
+ */
+ public void setAccountManagementDisabled(ComponentName admin, String accountType,
+ boolean disabled) {
+ if (mService != null) {
+ try {
+ mService.setAccountManagementDisabled(admin, accountType, disabled);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
* Called by profile or device owner to re-enable system apps by intent that were disabled
* by default when the managed profile was created. This should only be called from a profile
* or device owner running within a managed profile.
@@ -2081,4 +2103,26 @@ public class DevicePolicyManager {
}
return 0;
}
+
+ /**
+ * Gets the array of accounts for which account management is disabled by the profile owner.
+ *
+ * <p> Account management can be disabled/enabled by calling
+ * {@link #setAccountManagementDisabled}.
+ *
+ * @return a list of account types for which account management has been disabled.
+ *
+ * @see #setAccountManagementDisabled
+ */
+ public String[] getAccountTypesWithManagementDisabled() {
+ if (mService != null) {
+ try {
+ return mService.getAccountTypesWithManagementDisabled();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+
+ return null;
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index b30f1b9..0096580 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -126,4 +126,7 @@ interface IDevicePolicyManager {
void enableSystemApp(in ComponentName admin, in String packageName);
int enableSystemAppWithIntent(in ComponentName admin, in Intent intent);
+
+ void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
+ String[] getAccountTypesWithManagementDisabled();
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7c625bd..042ee28 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1982,6 +1982,7 @@ public abstract class Context {
WIFI_SERVICE,
WIFI_HOTSPOT_SERVICE,
WIFI_P2P_SERVICE,
+ WIFI_SCANNING_SERVICE,
NSD_SERVICE,
AUDIO_SERVICE,
MEDIA_ROUTER_SERVICE,
@@ -2054,6 +2055,9 @@ public abstract class Context {
* <dt> {@link #WIFI_SERVICE} ("wifi")
* <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of
* Wi-Fi connectivity.
+ * <dt> {@link #WIFI_P2P_SERVICE} ("wifip2p")
+ * <dd> A {@link android.net.wifi.p2p.WifiP2pManager WifiP2pManager} for management of
+ * Wi-Fi Direct connectivity.
* <dt> {@link #INPUT_METHOD_SERVICE} ("input_method")
* <dd> An {@link android.view.inputmethod.InputMethodManager InputMethodManager}
* for management of input methods.
@@ -2357,6 +2361,16 @@ public abstract class Context {
/**
* Use with {@link #getSystemService} to retrieve a {@link
+ * android.net.wifi.WifiScanner} for scanning the wifi universe
+ *
+ * @see #getSystemService
+ * @see android.net.wifi.WifiScanner
+ * @hide
+ */
+ public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
+
+ /**
+ * Use with {@link #getSystemService} to retrieve a {@link
* android.net.nsd.NsdManager} for handling management of network service
* discovery
*
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d5a3bcb..e0ac60b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2838,6 +2838,8 @@ public final class Settings {
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SAVED_STATE);
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS);
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_SUSPEND_OPTIMIZATIONS_ENABLED);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_ENHANCED_AUTO_JOIN);
+ MOVED_TO_GLOBAL.add(Settings.Global.WIFI_NETWORK_SHOW_RSSI);
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_ON);
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
MOVED_TO_GLOBAL.add(Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
@@ -5469,7 +5471,21 @@ public final class Settings {
public static final String WIFI_SUPPLICANT_SCAN_INTERVAL_MS =
"wifi_supplicant_scan_interval_ms";
- /**
+ /**
+ * whether frameworks handles wifi auto-join
+ * @hide
+ */
+ public static final String WIFI_ENHANCED_AUTO_JOIN =
+ "wifi_enhanced_auto_join";
+
+ /**
+ * whether settings show RSSI
+ * @hide
+ */
+ public static final String WIFI_NETWORK_SHOW_RSSI =
+ "wifi_network_show_rssi";
+
+ /**
* The interval in milliseconds to scan at supplicant when p2p is connected
* @hide
*/
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index cde8080..e4ad354 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -276,6 +276,8 @@ public class ShareActionProvider extends ActionProvider {
* @see Intent#ACTION_SEND_MULTIPLE
*/
public void setShareIntent(Intent shareIntent) {
+ shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
+ Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext,
mShareHistoryFileName);
dataModel.setIntent(shareIntent);
@@ -292,7 +294,8 @@ public class ShareActionProvider extends ActionProvider {
final int itemId = item.getItemId();
Intent launchIntent = dataModel.chooseActivity(itemId);
if (launchIntent != null) {
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
+ Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
mContext.startActivity(launchIntent);
}
return true;
@@ -308,7 +311,7 @@ public class ShareActionProvider extends ActionProvider {
return;
}
if (mOnChooseActivityListener == null) {
- mOnChooseActivityListener = new ShareAcitivityChooserModelPolicy();
+ mOnChooseActivityListener = new ShareActivityChooserModelPolicy();
}
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mShareHistoryFileName);
dataModel.setOnChooseActivityListener(mOnChooseActivityListener);
@@ -317,7 +320,7 @@ public class ShareActionProvider extends ActionProvider {
/**
* Policy that delegates to the {@link OnShareTargetSelectedListener}, if such.
*/
- private class ShareAcitivityChooserModelPolicy implements OnChooseActivityListener {
+ private class ShareActivityChooserModelPolicy implements OnChooseActivityListener {
@Override
public boolean onChooseActivity(ActivityChooserModel host, Intent intent) {
if (mOnShareTargetSelectedListener != null) {
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 8cdaf91..abd1791 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -18,156 +18,122 @@ package com.android.internal.app;
import android.app.Activity;
import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
+import android.graphics.Color;
import android.graphics.Typeface;
-import android.provider.Settings;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
-import android.text.method.AllCapsTransformationMethod;
+import android.provider.Settings;
+import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.View;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnticipateOvershootInterpolator;
-import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
-import android.widget.ImageView;
import android.widget.TextView;
public class PlatLogoActivity extends Activity {
- FrameLayout mContent;
- int mCount;
- final Handler mHandler = new Handler();
- static final int BGCOLOR = 0xffed1d24;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- DisplayMetrics metrics = new DisplayMetrics();
- getWindowManager().getDefaultDisplay().getMetrics(metrics);
-
- Typeface bold = Typeface.create("sans-serif", Typeface.BOLD);
- Typeface light = Typeface.create("sans-serif-light", Typeface.NORMAL);
-
- mContent = new FrameLayout(this);
- mContent.setBackgroundColor(0xC0000000);
-
- final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.WRAP_CONTENT,
- FrameLayout.LayoutParams.WRAP_CONTENT);
- lp.gravity = Gravity.CENTER;
-
- final ImageView logo = new ImageView(this);
- logo.setImageResource(com.android.internal.R.drawable.platlogo);
- logo.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- logo.setVisibility(View.INVISIBLE);
-
- final View bg = new View(this);
- bg.setBackgroundColor(BGCOLOR);
- bg.setAlpha(0f);
-
- final TextView letter = new TextView(this);
-
- letter.setTypeface(bold);
- letter.setTextSize(300);
- letter.setTextColor(0xFFFFFFFF);
- letter.setGravity(Gravity.CENTER);
- letter.setText(String.valueOf(Build.ID).substring(0, 1));
-
- final int p = (int)(4 * metrics.density);
-
- final TextView tv = new TextView(this);
- if (light != null) tv.setTypeface(light);
- tv.setTextSize(30);
- tv.setPadding(p, p, p, p);
- tv.setTextColor(0xFFFFFFFF);
- tv.setGravity(Gravity.CENTER);
- tv.setTransformationMethod(new AllCapsTransformationMethod(this));
- tv.setText("Android " + Build.VERSION.RELEASE);
- tv.setVisibility(View.INVISIBLE);
-
- mContent.addView(bg);
- mContent.addView(letter, lp);
- mContent.addView(logo, lp);
+ private static class Torso extends FrameLayout {
+ boolean mAnimate = false;
+ TextView mText;
+
+ public Torso(Context context) {
+ this(context, null);
+ }
+ public Torso(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+ public Torso(Context context, AttributeSet attrs, int flags) {
+ super(context, attrs, flags);
+
+ for (int i=0; i<2; i++) {
+ final View v = new View(context);
+ v.setBackgroundColor(i % 2 == 0 ? Color.BLUE : Color.RED);
+ addView(v);
+ }
- final FrameLayout.LayoutParams lp2 = new FrameLayout.LayoutParams(lp);
- lp2.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
- lp2.bottomMargin = 10*p;
+ mText = new TextView(context);
+ mText.setTextColor(Color.BLACK);
+ mText.setTextSize(14 /* sp */);
+ mText.setTypeface(Typeface.create("monospace", Typeface.BOLD));
- mContent.addView(tv, lp2);
+ addView(mText, new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ Gravity.BOTTOM | Gravity.LEFT
+ ));
+ }
- mContent.setOnClickListener(new View.OnClickListener() {
- int clicks;
+ private Runnable mRunnable = new Runnable() {
@Override
- public void onClick(View v) {
- clicks++;
- if (clicks >= 6) {
- mContent.performLongClick();
- return;
+ public void run() {
+ mText.setText(String.format("android_%s.flv - build %s",
+ Build.VERSION.CODENAME,
+ Build.VERSION.INCREMENTAL));
+ final int N = getChildCount();
+ final float parentw = getMeasuredWidth();
+ final float parenth = getMeasuredHeight();
+ for (int i=0; i<N; i++) {
+ final View v = getChildAt(i);
+ if (v instanceof TextView) continue;
+
+ final int w = (int) (Math.random() * parentw);
+ final int h = (int) (Math.random() * parenth);
+ v.setLayoutParams(new FrameLayout.LayoutParams(w, h));
+
+ v.setX((float) Math.random() * (parentw - w));
+ v.setY((float) Math.random() * (parenth - h));
}
- letter.animate().cancel();
- final float offset = (int)letter.getRotation() % 360;
- letter.animate()
- .rotationBy((Math.random() > 0.5f ? 360 : -360) - offset)
- .setInterpolator(new DecelerateInterpolator())
- .setDuration(700).start();
- }
- });
- mContent.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- if (logo.getVisibility() != View.VISIBLE) {
- bg.setScaleX(0.01f);
- bg.animate().alpha(1f).scaleX(1f).setStartDelay(500).start();
- letter.animate().alpha(0f).scaleY(0.5f).scaleX(0.5f)
- .rotationBy(360)
- .setInterpolator(new AccelerateInterpolator())
- .setDuration(1000)
- .start();
- logo.setAlpha(0f);
- logo.setVisibility(View.VISIBLE);
- logo.setScaleX(0.5f);
- logo.setScaleY(0.5f);
- logo.animate().alpha(1f).scaleX(1f).scaleY(1f)
- .setDuration(1000).setStartDelay(500)
- .setInterpolator(new AnticipateOvershootInterpolator())
- .start();
- tv.setAlpha(0f);
- tv.setVisibility(View.VISIBLE);
- tv.animate().alpha(1f).setDuration(1000).setStartDelay(1000).start();
- return true;
- }
- return false;
+ if (mAnimate) postDelayed(this, 1000);
}
- });
+ };
+ @Override
+ protected void onAttachedToWindow() {
+ mAnimate = true;
+ post(mRunnable);
+ }
+ @Override
+ protected void onDetachedFromWindow() {
+ mAnimate = false;
+ removeCallbacks(mRunnable);
+ }
+ }
- logo.setOnLongClickListener(new View.OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- if (Settings.System.getLong(getContentResolver(), Settings.System.EGG_MODE, 0)
- == 0) {
- // For posterity: the moment this user unlocked the easter egg
- Settings.System.putLong(getContentResolver(),
- Settings.System.EGG_MODE,
- System.currentTimeMillis());
- }
- try {
- startActivity(new Intent(Intent.ACTION_MAIN)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
- .addCategory("com.android.internal.category.PLATLOGO"));
- } catch (ActivityNotFoundException ex) {
- android.util.Log.e("PlatLogoActivity", "Couldn't catch a break.");
- }
- finish();
- return true;
- }
- });
-
- setContentView(mContent);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Torso t = new Torso(this);
+ t.setBackgroundColor(Color.WHITE);
+
+ t.getChildAt(0)
+ .setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ final ContentResolver cr = getContentResolver();
+ if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0)
+ == 0) {
+ // For posterity: the moment this user unlocked the easter egg
+ Settings.System.putLong(cr,
+ Settings.System.EGG_MODE,
+ System.currentTimeMillis());
+ }
+ try {
+ startActivity(new Intent(Intent.ACTION_MAIN)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ .addCategory("com.android.internal.category.PLATLOGO"));
+ } catch (ActivityNotFoundException ex) {
+ android.util.Log.e("PlatLogoActivity", "Couldn't catch a break.");
+ }
+ finish();
+ return true;
+ }
+ });
+
+ setContentView(t);
}
}
diff --git a/core/java/com/android/internal/util/LegacyNotificationUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java
index 0394bbc..f38cbde 100644
--- a/core/java/com/android/internal/util/LegacyNotificationUtil.java
+++ b/core/java/com/android/internal/util/NotificationColorUtil.java
@@ -24,6 +24,7 @@ import android.graphics.Color;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.TextAppearanceSpan;
@@ -38,21 +39,21 @@ import java.util.WeakHashMap;
*
* @hide
*/
-public class LegacyNotificationUtil {
+public class NotificationColorUtil {
- private static final String TAG = "LegacyNotificationUtil";
+ private static final String TAG = "NotificationColorUtil";
private static final Object sLock = new Object();
- private static LegacyNotificationUtil sInstance;
+ private static NotificationColorUtil sInstance;
private final ImageUtils mImageUtils = new ImageUtils();
private final WeakHashMap<Bitmap, Pair<Boolean, Integer>> mGrayscaleBitmapCache =
new WeakHashMap<Bitmap, Pair<Boolean, Integer>>();
- public static LegacyNotificationUtil getInstance() {
+ public static NotificationColorUtil getInstance() {
synchronized (sLock) {
if (sInstance == null) {
- sInstance = new LegacyNotificationUtil();
+ sInstance = new NotificationColorUtil();
}
return sInstance;
}
@@ -107,6 +108,9 @@ public class LegacyNotificationUtil {
AnimationDrawable ad = (AnimationDrawable) d;
int count = ad.getNumberOfFrames();
return count > 0 && isGrayscale(ad.getFrame(0));
+ } else if (d instanceof VectorDrawable) {
+ // We just assume you're doing the right thing if using vectors
+ return true;
} else {
return false;
}
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index b380403..bc92c4a 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -46,6 +46,8 @@ public class Protocol {
public static final int BASE_WIFI_MONITOR = 0x00024000;
public static final int BASE_WIFI_MANAGER = 0x00025000;
public static final int BASE_WIFI_CONTROLLER = 0x00026000;
+ public static final int BASE_WIFI_SCANNER = 0x00027000;
+ public static final int BASE_WIFI_SCANNER_SERVICE = 0x00027100;
public static final int BASE_DHCP = 0x00030000;
public static final int BASE_DATA_CONNECTION = 0x00040000;
public static final int BASE_DATA_CONNECTION_AC = 0x00041000;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 241500a..cdb77f1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1295,10 +1295,9 @@
<!-- @hide Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
that removes restrictions on where broadcasts can be sent and allows other
types of interactions. -->
- <!-- TODO: Remove the system protection level.-->
<permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="signature|system"
+ android:protectionLevel="signature"
android:label="@string/permlab_interactAcrossUsersFull"
android:description="@string/permdesc_interactAcrossUsersFull" />
diff --git a/core/res/res/drawable-hdpi/stat_sys_adb_am.png b/core/res/res/drawable-hdpi/stat_sys_adb_am.png
deleted file mode 100644
index dad614c..0000000
--- a/core/res/res/drawable-hdpi/stat_sys_adb_am.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/stat_sys_adb_am.png b/core/res/res/drawable-ldpi/stat_sys_adb_am.png
deleted file mode 100644
index 0171adb..0000000
--- a/core/res/res/drawable-ldpi/stat_sys_adb_am.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_adb_am.png b/core/res/res/drawable-mdpi/stat_sys_adb_am.png
deleted file mode 100644
index 5482f34..0000000
--- a/core/res/res/drawable-mdpi/stat_sys_adb_am.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.png b/core/res/res/drawable-nodpi/platlogo.png
deleted file mode 100644
index 6351c2d..0000000
--- a/core/res/res/drawable-nodpi/platlogo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
new file mode 100644
index 0000000..8eb00fa
--- /dev/null
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -0,0 +1,39 @@
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size android:width="400dp" android:height="400dp"/>
+
+ <viewport android:viewportHeight="25" android:viewportWidth="25" />
+
+ <group>
+ <path
+ android:name="shadow"
+ android:pathData="m12,2.5 a11,11 0 1,0 1,0
+ M6.5,7.5
+ l5,0 l0,7 l7,0 l0,5 l-12,0 z"
+ android:fill="#40000000"
+ />
+ <path
+ android:name="circle-L-ranch"
+ android:pathData="m12,1.5 a11,11 0 1,0 1,0
+ M6.5,6.5
+ l5,0 l0,7 l7,0 l0,5 l-12,0 z"
+ android:fill="#FFFFFF40"
+ />
+ </group>
+</vector>
+
+
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
new file mode 100644
index 0000000..37df348
--- /dev/null
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -0,0 +1,30 @@
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size android:width="25dp" android:height="25dp"/>
+
+ <viewport android:viewportHeight="25" android:viewportWidth="25" />
+
+ <group>
+ <path
+ android:name="adb"
+ android:pathData="m3,3l8,0l0,11l11,0l0,8l-18,0z"
+ android:fill="#FFFFFFFF"
+ />
+ </group>
+</vector>
+
+
diff --git a/core/res/res/drawable-xhdpi/stat_sys_adb_am.png b/core/res/res/drawable-xhdpi/stat_sys_adb_am.png
deleted file mode 100644
index e53f498..0000000
--- a/core/res/res/drawable-xhdpi/stat_sys_adb_am.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png b/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png
deleted file mode 100644
index d6018dd..0000000
--- a/core/res/res/drawable-xxhdpi/stat_sys_adb_am.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/notification_icon_legacy_bg.xml b/core/res/res/drawable/notification_icon_legacy_bg.xml
index 4ac67c3..cc5755d 100644
--- a/core/res/res/drawable/notification_icon_legacy_bg.xml
+++ b/core/res/res/drawable/notification_icon_legacy_bg.xml
@@ -18,5 +18,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
- android:color="@color/notification_icon_legacy_bg_color"/>
+ android:color="@color/notification_icon_bg_color"/>
</shape>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 761170d..7d3fb44 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -123,7 +123,7 @@
<drawable name="notification_template_icon_bg">#3333B5E5</drawable>
<drawable name="notification_template_icon_low_bg">#0cffffff</drawable>
- <color name="notification_icon_legacy_bg_color">#ff4285F4</color>
+ <color name="notification_icon_bg_color">#ffa3a3a3</color>
<color name="notification_action_legacy_color_filter">#ff555555</color>
<!-- Keyguard colors -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a8a4b51..2bf72e8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1664,6 +1664,7 @@
<java-symbol type="layout" name="notification_template_quantum_big_text" />
<java-symbol type="layout" name="notification_template_quantum_inbox" />
<java-symbol type="color" name="notification_action_legacy_color_filter" />
+ <java-symbol type="color" name="notification_icon_bg_color" />
<java-symbol type="drawable" name="notification_icon_legacy_bg_inset" />
<java-symbol type="drawable" name="notification_quantum_bg_dim" />
<java-symbol type="drawable" name="notification_quantum_bg" />
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index 2e08bff..30eedee 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -18,7 +18,7 @@
<com.android.systemui.statusbar.NotificationOverflowContainer
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="32dp"
+ android:layout_height="40dp"
android:focusable="true"
android:clickable="true"
>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index e5168c4..4369741 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -18,11 +18,13 @@
<resources>
<item type="id" name="translation_y_animator_tag"/>
<item type="id" name="translation_z_animator_tag"/>
+ <item type="id" name="scale_animator_tag"/>
<item type="id" name="alpha_animator_tag"/>
<item type="id" name="top_inset_animator_tag"/>
<item type="id" name="height_animator_tag"/>
<item type="id" name="translation_y_animator_end_value_tag"/>
<item type="id" name="translation_z_animator_end_value_tag"/>
+ <item type="id" name="scale_animator_end_value_tag"/>
<item type="id" name="alpha_animator_end_value_tag"/>
<item type="id" name="top_inset_animator_end_value_tag"/>
<item type="id" name="height_animator_end_value_tag"/>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 3371290..91df9ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -38,6 +38,7 @@ import com.android.internal.R;
public abstract class ActivatableNotificationView extends ExpandableOutlineView {
private static final long DOUBLETAP_TIMEOUT_MS = 1000;
+ private static final int BACKGROUND_ANIMATION_LENGTH_MS = 220;
private boolean mDimmed;
@@ -181,7 +182,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
mActivated = false;
}
if (mOnActivatedListener != null) {
- mOnActivatedListener.onReset(this);
+ mOnActivatedListener.onActivationReset(this);
}
removeCallbacks(mTapTimeoutRunnable);
}
@@ -191,12 +192,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
&& Math.abs(event.getY() - mDownY) < mTouchSlop;
}
- /**
- * Sets the notification as dimmed, meaning that it will appear in a more gray variant.
- *
- * @param dimmed Whether the notification should be dimmed.
- * @param fade Whether an animation should be played to change the state.
- */
public void setDimmed(boolean dimmed, boolean fade) {
if (mDimmed != dimmed) {
mDimmed = dimmed;
@@ -228,7 +223,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
int startAlpha = mDimmed ? 255 : 0;
int endAlpha = mDimmed ? 0 : 255;
- int duration = NotificationActivator.ANIMATION_LENGTH_MS;
+ int duration = BACKGROUND_ANIMATION_LENGTH_MS;
// Check whether there is already a background animation running.
if (mBackgroundAnimator != null) {
startAlpha = (Integer) mBackgroundAnimator.getAnimatedValue();
@@ -315,8 +310,8 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
@Override
- public void setActualHeight(int actualHeight) {
- super.setActualHeight(actualHeight);
+ public void setActualHeight(int actualHeight, boolean notifyListeners) {
+ super.setActualHeight(actualHeight, notifyListeners);
invalidate();
setPivotY(actualHeight / 2);
}
@@ -333,6 +328,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
public interface OnActivatedListener {
void onActivated(View view);
- void onReset(View view);
+ void onActivationReset(View view);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index a325186..ecefc39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -70,7 +70,7 @@ import android.widget.TextView;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
-import com.android.internal.util.LegacyNotificationUtil;
+import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SearchPanelView;
@@ -143,7 +143,7 @@ public abstract class BaseStatusBar extends SystemUI implements
// public mode, private notifications, etc
private boolean mLockscreenPublicMode = false;
private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
- private LegacyNotificationUtil mLegacyNotificationUtil = LegacyNotificationUtil.getInstance();
+ private NotificationColorUtil mNotificationColorUtil = NotificationColorUtil.getInstance();
private UserManager mUserManager;
@@ -852,7 +852,7 @@ public abstract class BaseStatusBar extends SystemUI implements
Drawable iconDrawable = StatusBarIconView.getIcon(mContext, ic);
icon.setImageDrawable(iconDrawable);
- if (mLegacyNotificationUtil.isGrayscale(iconDrawable)) {
+ if (mNotificationColorUtil.isGrayscale(iconDrawable)) {
icon.setBackgroundResource(
com.android.internal.R.drawable.notification_icon_legacy_bg_inset);
}
@@ -1067,7 +1067,6 @@ public abstract class BaseStatusBar extends SystemUI implements
entry.row.setSystemExpanded(top);
}
}
- entry.row.setDimmed(onKeyguard, false /* fade */);
boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
if (onKeyguard && (visibleNotifications >= maxKeyguardNotifications
|| !showOnKeyguard)) {
@@ -1087,48 +1086,11 @@ public abstract class BaseStatusBar extends SystemUI implements
if (onKeyguard && mKeyguardIconOverflowContainer.getIconsView().getChildCount() > 0) {
mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE);
- mKeyguardIconOverflowContainer.setDimmed(true /* dimmed */, false /* fade */);
} else {
mKeyguardIconOverflowContainer.setVisibility(View.GONE);
}
}
- @Override
- public void onActivated(View view) {
- int n = mNotificationData.size();
- for (int i = 0; i < n; i++) {
- NotificationData.Entry entry = mNotificationData.get(i);
- if (entry.row.getVisibility() != View.GONE) {
- if (view == entry.row) {
- entry.row.getActivator().activate();
- } else {
- entry.row.getActivator().activateInverse();
- }
- }
- }
- if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
- if (view == mKeyguardIconOverflowContainer) {
- mKeyguardIconOverflowContainer.getActivator().activate();
- } else {
- mKeyguardIconOverflowContainer.getActivator().activateInverse();
- }
- }
- }
-
- @Override
- public void onReset(View view) {
- int n = mNotificationData.size();
- for (int i = 0; i < n; i++) {
- NotificationData.Entry entry = mNotificationData.get(i);
- if (entry.row.getVisibility() != View.GONE) {
- entry.row.getActivator().reset();
- }
- }
- if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
- mKeyguardIconOverflowContainer.getActivator().reset();
- }
- }
-
private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
return sbn.getNotification().priority >= Notification.PRIORITY_LOW;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index e471754..5b2ea0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -117,7 +117,7 @@ public class DragDownHelper implements Gefingerpoken {
} else {
if (mDraggedFarEnough) {
mDraggedFarEnough = false;
- mOnDragDownListener.onReset();
+ mOnDragDownListener.onDragDownReset();
}
}
return true;
@@ -188,7 +188,7 @@ public class DragDownHelper implements Gefingerpoken {
cancelExpansion(mStartingChild);
}
mDraggingDown = false;
- mOnDragDownListener.onReset();
+ mOnDragDownListener.onDragDownReset();
}
private ExpandableView findView(float x, float y) {
@@ -200,7 +200,7 @@ public class DragDownHelper implements Gefingerpoken {
public interface OnDragDownListener {
void onDraggedDown(View startingChild);
- void onReset();
+ void onDragDownReset();
void onThresholdReached();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index e5512a3..39f2bb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -52,7 +52,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
private NotificationContentView mPublicLayout;
private NotificationContentView mPrivateLayout;
private int mMaxExpandHeight;
- private NotificationActivator mActivator;
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -63,8 +62,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
super.onFinishInflate();
mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic);
mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
-
- mActivator = new NotificationActivator(this, this);
}
@Override
@@ -208,23 +205,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
mPrivateLayout.setVisibility(show ? View.GONE : View.VISIBLE);
}
- /**
- * Sets the notification as dimmed, meaning that it will appear in a more gray variant.
- */
- @Override
- public void setDimmed(boolean dimmed, boolean fade) {
- super.setDimmed(dimmed, fade);
- mActivator.setDimmed(dimmed, fade);
- }
-
public int getMaxExpandHeight() {
return mMaxExpandHeight;
}
- public NotificationActivator getActivator() {
- return mActivator;
- }
-
/**
* @return the potential height this view could expand in addition.
*/
@@ -238,10 +222,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
}
@Override
- public void setActualHeight(int height) {
- mPrivateLayout.setActualHeight(height);
+ public void setActualHeight(int height, boolean notifyListeners) {
+ mPrivateLayout.setActualHeight(height, notifyListeners);
invalidate();
- super.setActualHeight(height);
+ super.setActualHeight(height, notifyListeners);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index 43eb5b5..a42c194 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -33,8 +33,8 @@ public abstract class ExpandableOutlineView extends ExpandableView {
}
@Override
- public void setActualHeight(int actualHeight) {
- super.setActualHeight(actualHeight);
+ public void setActualHeight(int actualHeight, boolean notifyListeners) {
+ super.setActualHeight(actualHeight, notifyListeners);
updateOutline();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 169521d..281bd2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -61,10 +61,19 @@ public abstract class ExpandableView extends FrameLayout {
/**
* Sets the actual height of this notification. This is different than the laid out
* {@link View#getHeight()}, as we want to avoid layouting during scrolling and expanding.
+ *
+ * @param actualHeight The height of this notification.
+ * @param notifyListeners Whether the listener should be informed about the change.
*/
- public void setActualHeight(int actualHeight) {
+ public void setActualHeight(int actualHeight, boolean notifyListeners) {
mActualHeight = actualHeight;
- notifyHeightChanged();
+ if (notifyListeners) {
+ notifyHeightChanged();
+ }
+ }
+
+ public void setActualHeight(int actualHeight) {
+ setActualHeight(actualHeight, true);
}
/**
@@ -91,6 +100,15 @@ public abstract class ExpandableView extends FrameLayout {
}
/**
+ * Sets the notification as dimmed. The default implementation does nothing.
+ *
+ * @param dimmed Whether the notification should be dimmed.
+ * @param fade Whether an animation should be played to change the state.
+ */
+ public void setDimmed(boolean dimmed, boolean fade) {
+ }
+
+ /**
* @return The desired notification height.
*/
public int getIntrinsicHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java
deleted file mode 100644
index a03aeec..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationActivator.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.statusbar;
-
-import android.content.Context;
-import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.android.systemui.R;
-
-/**
- * A helper class used by both {@link com.android.systemui.statusbar.ExpandableNotificationRow} and
- * {@link com.android.systemui.statusbar.NotificationOverflowIconsView} to make a notification look
- * active after tapping it once on the Keyguard.
- */
-public class NotificationActivator {
-
- public static final int ANIMATION_LENGTH_MS = 220;
- private static final float INVERSE_ALPHA = 0.9f;
- private static final float DIMMED_SCALE = 0.95f;
-
- /**
- * Normal state. Notification is fully interactable.
- */
- private static final int STATE_NORMAL = 0;
-
- /**
- * Dimmed state. Neutral state when on the lockscreen, with slight transparency and scaled down
- * a bit.
- */
- private static final int STATE_DIMMED = 1;
-
- /**
- * Activated state. Used after tapping a notification on the lockscreen. Normal transparency and
- * normal scale.
- */
- private static final int STATE_ACTIVATED = 2;
-
- /**
- * Inverse activated state. Used for the other notifications on the lockscreen when tapping on
- * one.
- */
- private static final int STATE_ACTIVATED_INVERSE = 3;
-
- private final View mTargetView;
- private final View mHotspotView;
- private final Interpolator mFastOutSlowInInterpolator;
- private final Interpolator mLinearOutSlowInInterpolator;
- private final int mTranslationZ;
-
- private int mState;
-
- public NotificationActivator(View targetView, View hotspotView) {
- mTargetView = targetView;
- mHotspotView = hotspotView;
- Context ctx = targetView.getContext();
- mFastOutSlowInInterpolator =
- AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in);
- mLinearOutSlowInInterpolator =
- AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in);
- mTranslationZ =
- ctx.getResources().getDimensionPixelSize(R.dimen.z_distance_between_notifications);
- mTargetView.animate().setDuration(ANIMATION_LENGTH_MS);
- }
-
- public void activateInverse() {
- if (mState == STATE_ACTIVATED_INVERSE) {
- return;
- }
- mTargetView.animate().cancel();
- mTargetView.animate().withLayer().alpha(INVERSE_ALPHA);
- mState = STATE_ACTIVATED_INVERSE;
- }
-
- public void addHotspot() {
- mHotspotView.getBackground().setHotspot(
- 0, mHotspotView.getWidth()/2, mHotspotView.getHeight()/2);
- }
-
- public void activate() {
- if (mState == STATE_ACTIVATED) {
- return;
- }
- mTargetView.animate().cancel();
- mTargetView.animate()
- .setInterpolator(mLinearOutSlowInInterpolator)
- .scaleX(1)
- .scaleY(1)
- .translationZBy(mTranslationZ);
- mState = STATE_ACTIVATED;
- }
-
- public void reset() {
- if (mState == STATE_DIMMED) {
- return;
- }
- mTargetView.animate().cancel();
- mTargetView.animate()
- .setInterpolator(mFastOutSlowInInterpolator)
- .scaleX(DIMMED_SCALE)
- .scaleY(DIMMED_SCALE)
- .translationZBy(-mTranslationZ);
- if (mTargetView.getAlpha() != 1.0f) {
- mTargetView.animate().withLayer().alpha(1);
- }
- mState = STATE_DIMMED;
- }
-
- public void setDimmed(boolean dimmed, boolean fade) {
- if (dimmed) {
- mTargetView.animate().cancel();
- if (fade) {
- mTargetView.animate()
- .setInterpolator(mFastOutSlowInInterpolator)
- .scaleX(DIMMED_SCALE)
- .scaleY(DIMMED_SCALE);
- } else {
- mTargetView.setScaleX(DIMMED_SCALE);
- mTargetView.setScaleY(DIMMED_SCALE);
- }
- mState = STATE_DIMMED;
- } else {
- mTargetView.animate().cancel();
- if (fade) {
- mTargetView.animate()
- .setInterpolator(mFastOutSlowInInterpolator)
- .scaleX(1)
- .scaleY(1);
- } else {
- mTargetView.animate().cancel();
- mTargetView.setScaleX(1);
- mTargetView.setScaleY(1);
- }
- mState = STATE_NORMAL;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 379bd05..9df2701 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -70,8 +70,8 @@ public class NotificationContentView extends ExpandableView {
}
@Override
- public void setActualHeight(int actualHeight) {
- super.setActualHeight(actualHeight);
+ public void setActualHeight(int actualHeight, boolean notifyListeners) {
+ super.setActualHeight(actualHeight, notifyListeners);
selectLayout();
updateClipping();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index e6b5600..864c597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -28,14 +28,13 @@ import com.android.systemui.R;
public class NotificationOverflowContainer extends ActivatableNotificationView {
private NotificationOverflowIconsView mIconsView;
- private NotificationActivator mActivator;
public NotificationOverflowContainer(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
- public void setActualHeight(int currentHeight) {
+ public void setActualHeight(int currentHeight, boolean notifyListeners) {
// noop
}
@@ -54,22 +53,9 @@ public class NotificationOverflowContainer extends ActivatableNotificationView {
super.onFinishInflate();
mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
mIconsView.setMoreText((TextView) findViewById(R.id.more_text));
-
- mActivator = new NotificationActivator(this, this);
- setDimmed(true, false);
- }
-
- @Override
- public void setDimmed(boolean dimmed, boolean fade) {
- super.setDimmed(dimmed, fade);
- mActivator.setDimmed(dimmed, fade);
}
public NotificationOverflowIconsView getIconsView() {
return mIconsView;
}
-
- public NotificationActivator getActivator() {
- return mActivator;
- }
}
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 c4ee6ff..92eee4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -628,8 +628,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
mKeyguardIconOverflowContainer.setOnActivatedListener(this);
mKeyguardCarrierLabel = mStatusBarWindow.findViewById(R.id.keyguard_carrier_text);
- // TODO: Comment in when transition is ready.
- //mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
+ mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
mStackScroller.addView(mKeyguardIconOverflowContainer);
mExpandedContents = mStackScroller;
@@ -2790,6 +2789,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mSettingsContainer.setKeyguardShowing(false);
}
+ updateStackScrollerState();
updatePublicMode();
updateRowStates();
checkBarModes();
@@ -2797,6 +2797,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
updateCarrierLabelVisibility(false);
}
+ public void updateStackScrollerState() {
+ mStackScroller.setDimmed(mState == StatusBarState.KEYGUARD, false /* animate */);
+ }
+
public void userActivity() {
if (mState == StatusBarState.KEYGUARD) {
mKeyguardViewMediatorCallback.userActivity();
@@ -2850,7 +2854,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
public void onActivated(View view) {
userActivity();
mKeyguardIndicationTextView.switchIndication(R.string.notification_tap_again);
- super.onActivated(view);
+ mStackScroller.setActivatedChild(view);
}
/**
@@ -2862,9 +2866,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
@Override
- public void onReset(View view) {
- super.onReset(view);
- mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
+ public void onActivationReset(View view) {
+ if (view == mStackScroller.getActivatedChild()) {
+ mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
+ mStackScroller.setActivatedChild(null);
+ }
}
public void onTrackingStarted() {
@@ -2896,30 +2902,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
@Override
- public void onReset() {
- int n = mNotificationData.size();
- for (int i = 0; i < n; i++) {
- NotificationData.Entry entry = mNotificationData.get(i);
- if (entry.row.getVisibility() != View.GONE) {
- entry.row.setDimmed(true /* dimmed */, true /* fade */);
- }
- }
- if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
- mKeyguardIconOverflowContainer.setDimmed(true /* dimmed */, true /* fade */);
- }
+ public void onDragDownReset() {
+ mStackScroller.setDimmed(true /* dimmed */, true /* animated */);
}
public void onThresholdReached() {
- int n = mNotificationData.size();
- for (int i = 0; i < n; i++) {
- NotificationData.Entry entry = mNotificationData.get(i);
- if (entry.row.getVisibility() != View.GONE) {
- entry.row.setDimmed(false /* dimmed */, true /* fade */);
- }
- }
- if (mKeyguardIconOverflowContainer.getVisibility() != View.GONE) {
- mKeyguardIconOverflowContainer.setDimmed(false /* dimmed */, true /* fade */);
- }
+ mStackScroller.setDimmed(false /* dimmed */, true /* animate */);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
new file mode 100644
index 0000000..4121a40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar.stack;
+
+import android.view.View;
+
+import java.util.ArrayList;
+
+/**
+ * A global state to track all input states for the algorithm.
+ */
+public class AmbientState {
+ private ArrayList<View> mDraggedViews = new ArrayList<View>();
+ private int mScrollY;
+ private boolean mDimmed;
+ private View mActivatedChild;
+
+ public int getScrollY() {
+ return mScrollY;
+ }
+
+ public void setScrollY(int scrollY) {
+ this.mScrollY = scrollY;
+ }
+
+ public void onBeginDrag(View view) {
+ mDraggedViews.add(view);
+ }
+
+ public void onDragFinished(View view) {
+ mDraggedViews.remove(view);
+ }
+
+ public ArrayList<View> getDraggedViews() {
+ return mDraggedViews;
+ }
+
+ /**
+ * @param dimmed Whether we are in a dimmed state (on the lockscreen), where the backgrounds are
+ * translucent and everything is scaled back a bit.
+ */
+ public void setDimmed(boolean dimmed) {
+ mDimmed = dimmed;
+ }
+
+ /**
+ * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap
+ * interaction. This child is then scaled normally and its background is fully opaque.
+ */
+ public void setActivatedChild(View activatedChild) {
+ mActivatedChild = activatedChild;
+ }
+
+ public boolean isDimmed() {
+ return mDimmed;
+ }
+
+ public View getActivatedChild() {
+ return mActivatedChild;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
new file mode 100644
index 0000000..41914ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar.stack;
+
+import java.util.ArrayList;
+
+/**
+ * Filters the animations for only a certain type of properties.
+ */
+public class AnimationFilter {
+ boolean animateAlpha;
+ boolean animateY;
+ boolean animateZ;
+ boolean animateScale;
+ boolean animateHeight;
+ boolean animateDimmed;
+
+ public AnimationFilter animateAlpha() {
+ animateAlpha = true;
+ return this;
+ }
+
+ public AnimationFilter animateY() {
+ animateY = true;
+ return this;
+ }
+
+ public AnimationFilter animateZ() {
+ animateZ = true;
+ return this;
+ }
+
+ public AnimationFilter animateScale() {
+ animateScale = true;
+ return this;
+ }
+
+ public AnimationFilter animateHeight() {
+ animateHeight = true;
+ return this;
+ }
+
+ public AnimationFilter animateDimmed() {
+ animateDimmed = true;
+ return this;
+ }
+
+ /**
+ * Combines multiple filters into {@code this} filter, using or as the operand .
+ *
+ * @param events The animation events from the filters to combine.
+ */
+ public void applyCombination(ArrayList<NotificationStackScrollLayout.AnimationEvent> events) {
+ reset();
+ int size = events.size();
+ for (int i = 0; i < size; i++) {
+ combineFilter(events.get(i).filter);
+ }
+ }
+
+ private void combineFilter(AnimationFilter filter) {
+ animateAlpha |= filter.animateAlpha;
+ animateY |= filter.animateY;
+ animateZ |= filter.animateZ;
+ animateScale |= filter.animateScale;
+ animateHeight |= filter.animateHeight;
+ animateDimmed |= filter.animateDimmed;
+ }
+
+ private void reset() {
+ animateAlpha = false;
+ animateY = false;
+ animateZ = false;
+ animateScale = false;
+ animateHeight = false;
+ animateDimmed = false;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 59d717c..afd5068 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -84,7 +84,6 @@ public class NotificationStackScrollLayout extends ViewGroup
private int mEmptyMarginBottom;
private int mPaddingBetweenElements;
private int mTopPadding;
- private boolean mListenForHeightChanges = true;
/**
* The algorithm which calculates the properties for our children
@@ -95,6 +94,7 @@ public class NotificationStackScrollLayout extends ViewGroup
* The current State this Layout is in
*/
private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
+ private AmbientState mAmbientState = new AmbientState();
private ArrayList<View> mChildrenToAddAnimated = new ArrayList<View>();
private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>();
private ArrayList<View> mSnappedBackChildren = new ArrayList<View>();
@@ -108,6 +108,8 @@ public class NotificationStackScrollLayout extends ViewGroup
private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
private boolean mNeedsAnimation;
private boolean mTopPaddingNeedsAnimation;
+ private boolean mDimmedNeedsAnimation;
+ private boolean mActivateNeedsAnimation;
private boolean mIsExpanded = true;
private boolean mChildrenUpdateRequested;
private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
@@ -267,8 +269,8 @@ public class NotificationStackScrollLayout extends ViewGroup
* modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
*/
private void updateChildren() {
- mCurrentStackScrollState.setScrollY(mOwnScrollY);
- mStackScrollAlgorithm.getStackScrollState(mCurrentStackScrollState);
+ mAmbientState.setScrollY(mOwnScrollY);
+ mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
if (!isCurrentlyAnimating() && !mNeedsAnimation) {
applyCurrentState();
} else {
@@ -385,12 +387,12 @@ public class NotificationStackScrollLayout extends ViewGroup
mDragAnimPendingChildren.remove(v);
}
mSwipedOutViews.add(v);
- mStackScrollAlgorithm.onDragFinished(v);
+ mAmbientState.onDragFinished(v);
}
@Override
public void onChildSnappedBack(View animView) {
- mStackScrollAlgorithm.onDragFinished(animView);
+ mAmbientState.onDragFinished(animView);
if (!mDragAnimPendingChildren.contains(animView)) {
mSnappedBackChildren.add(animView);
requestChildrenUpdate();
@@ -404,7 +406,7 @@ public class NotificationStackScrollLayout extends ViewGroup
public void onBeginDrag(View v) {
setSwipingInProgress(true);
mDragAnimPendingChildren.add(v);
- mStackScrollAlgorithm.onBeginDrag(v);
+ mAmbientState.onBeginDrag(v);
requestChildrenUpdate();
mNeedsAnimation = true;
}
@@ -514,7 +516,7 @@ public class NotificationStackScrollLayout extends ViewGroup
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
- if (getChildCount() == 0) {
+ if (getChildCount() == 0 || !isInContentBounds(ev)) {
return false;
}
boolean isBeingDragged = !mScroller.isFinished();
@@ -965,6 +967,8 @@ public class NotificationStackScrollLayout extends ViewGroup
generateSnapBackEvents();
generateDragEvents();
generateTopPaddingEvent();
+ generateActivateEvent();
+ generateDimmedEvent();
mNeedsAnimation = false;
}
@@ -1012,6 +1016,22 @@ public class NotificationStackScrollLayout extends ViewGroup
mTopPaddingNeedsAnimation = false;
}
+ private void generateActivateEvent() {
+ if (mActivateNeedsAnimation) {
+ mAnimationEvents.add(
+ new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_ACTIVATED_CHILD));
+ }
+ mActivateNeedsAnimation = false;
+ }
+
+ private void generateDimmedEvent() {
+ if (mDimmedNeedsAnimation) {
+ mAnimationEvents.add(
+ new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_DIMMED));
+ }
+ mDimmedNeedsAnimation = false;
+ }
+
private boolean onInterceptTouchEventScroll(MotionEvent ev) {
/*
* This method JUST determines whether we want to intercept the motion.
@@ -1120,6 +1140,13 @@ public class NotificationStackScrollLayout extends ViewGroup
return mIsBeingDragged;
}
+ /**
+ * @return Whether the specified motion event is actually happening over the content.
+ */
+ private boolean isInContentBounds(MotionEvent event) {
+ return event.getY() < getHeight() - getEmptyBottomMargin();
+ }
+
private void setIsBeingDragged(boolean isDragged) {
mIsBeingDragged = isDragged;
if (isDragged) {
@@ -1177,14 +1204,12 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
public void onHeightChanged(ExpandableView view) {
- if (mListenForHeightChanges && !isCurrentlyAnimating()) {
- updateContentHeight();
- updateScrollPositionIfNecessary();
- if (mOnHeightChangedListener != null) {
- mOnHeightChangedListener.onHeightChanged(view);
- }
- requestChildrenUpdate();
+ updateContentHeight();
+ updateScrollPositionIfNecessary();
+ if (mOnHeightChangedListener != null) {
+ mOnHeightChangedListener.onHeightChanged(view);
}
+ requestChildrenUpdate();
}
public void setOnHeightChangedListener(
@@ -1197,10 +1222,34 @@ public class NotificationStackScrollLayout extends ViewGroup
mAnimationEvents.clear();
}
+ /**
+ * See {@link AmbientState#setDimmed}.
+ */
+ public void setDimmed(boolean dimmed, boolean animate) {
+ mAmbientState.setDimmed(dimmed);
+ if (animate) {
+ mDimmedNeedsAnimation = true;
+ mNeedsAnimation = true;
+ }
+ requestChildrenUpdate();
+ }
+
+ /**
+ * See {@link AmbientState#setActivatedChild}.
+ */
+ public void setActivatedChild(View activatedChild) {
+ mAmbientState.setActivatedChild(activatedChild);
+ mActivateNeedsAnimation = true;
+ mNeedsAnimation = true;
+ requestChildrenUpdate();
+ }
+
+ public View getActivatedChild() {
+ return mAmbientState.getActivatedChild();
+ }
+
private void applyCurrentState() {
- mListenForHeightChanges = false;
mCurrentStackScrollState.apply();
- mListenForHeightChanges = true;
if (mListener != null) {
mListener.onChildLocationsChanged(this);
}
@@ -1215,21 +1264,120 @@ public class NotificationStackScrollLayout extends ViewGroup
static class AnimationEvent {
- static int ANIMATION_TYPE_ADD = 1;
- static int ANIMATION_TYPE_REMOVE = 2;
- static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 3;
- static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 4;
- static int ANIMATION_TYPE_START_DRAG = 5;
- static int ANIMATION_TYPE_SNAP_BACK = 6;
+ static AnimationFilter[] FILTERS = new AnimationFilter[] {
+
+ // ANIMATION_TYPE_ADD
+ new AnimationFilter()
+ .animateAlpha()
+ .animateHeight()
+ .animateY()
+ .animateZ(),
+
+ // ANIMATION_TYPE_REMOVE
+ new AnimationFilter()
+ .animateAlpha()
+ .animateHeight()
+ .animateY()
+ .animateZ(),
+
+ // ANIMATION_TYPE_REMOVE_SWIPED_OUT
+ new AnimationFilter()
+ .animateAlpha()
+ .animateHeight()
+ .animateY()
+ .animateZ(),
+
+ // ANIMATION_TYPE_TOP_PADDING_CHANGED
+ new AnimationFilter()
+ .animateAlpha()
+ .animateHeight()
+ .animateY()
+ .animateDimmed()
+ .animateScale()
+ .animateZ(),
+
+ // ANIMATION_TYPE_START_DRAG
+ new AnimationFilter()
+ .animateAlpha(),
+
+ // ANIMATION_TYPE_SNAP_BACK
+ new AnimationFilter()
+ .animateAlpha(),
+
+ // ANIMATION_TYPE_ACTIVATED_CHILD
+ new AnimationFilter()
+ .animateScale()
+ .animateAlpha(),
+
+ // ANIMATION_TYPE_DIMMED
+ new AnimationFilter()
+ .animateScale()
+ .animateDimmed()
+ };
+
+ static int[] LENGTHS = new int[] {
+
+ // ANIMATION_TYPE_ADD
+ StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+ // ANIMATION_TYPE_REMOVE
+ StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+ // ANIMATION_TYPE_REMOVE_SWIPED_OUT
+ StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+ // ANIMATION_TYPE_TOP_PADDING_CHANGED
+ StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+ // ANIMATION_TYPE_START_DRAG
+ StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+ // ANIMATION_TYPE_SNAP_BACK
+ StackStateAnimator.ANIMATION_DURATION_STANDARD,
+
+ // ANIMATION_TYPE_ACTIVATED_CHILD
+ StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
+
+ // ANIMATION_TYPE_DIMMED
+ StackStateAnimator.ANIMATION_DURATION_DIMMED_ACTIVATED,
+ };
+
+ static int ANIMATION_TYPE_ADD = 0;
+ static int ANIMATION_TYPE_REMOVE = 1;
+ static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 2;
+ static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 3;
+ static int ANIMATION_TYPE_START_DRAG = 4;
+ static int ANIMATION_TYPE_SNAP_BACK = 5;
+ static int ANIMATION_TYPE_ACTIVATED_CHILD = 6;
+ static int ANIMATION_TYPE_DIMMED = 7;
final long eventStartTime;
final View changingView;
final int animationType;
+ final AnimationFilter filter;
+ final long length;
AnimationEvent(View view, int type) {
eventStartTime = AnimationUtils.currentAnimationTimeMillis();
changingView = view;
animationType = type;
+ filter = FILTERS[type];
+ length = LENGTHS[type];
+ }
+
+ /**
+ * Combines the length of several animation events into a single value.
+ *
+ * @param events The events of the lengths to combine.
+ * @return The combined length. This is just the maximum of all length.
+ */
+ static long combineLength(ArrayList<AnimationEvent> events) {
+ long length = 0;
+ int size = events.size();
+ for (int i = 0; i < size; i++) {
+ length = Math.max(length, events.get(i).length);
+ }
+ return length;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index f7818c0..5e4d496 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -39,6 +39,10 @@ public class StackScrollAlgorithm {
private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3;
private static final int MAX_ITEMS_IN_TOP_STACK = 3;
+ /** When a child is activated, the other cards' alpha fade to this value. */
+ private static final float ACTIVATED_INVERSE_ALPHA = 0.9f;
+ private static final float DIMMED_SCALE = 0.95f;
+
private int mPaddingBetweenElements;
private int mCollapsedSize;
private int mTopStackPeekSize;
@@ -61,7 +65,6 @@ public class StackScrollAlgorithm {
private ExpandableView mFirstChildWhileExpanding;
private boolean mExpandedOnStart;
private int mTopStackTotalSize;
- private ArrayList<View> mDraggedViews = new ArrayList<View>();
public StackScrollAlgorithm(Context context) {
initConstants(context);
@@ -93,7 +96,7 @@ public class StackScrollAlgorithm {
}
- public void getStackScrollState(StackScrollState resultState) {
+ public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
// The state of the local variables are saved in an algorithmState to easily subdivide it
// into multiple phases.
StackScrollAlgorithmState algorithmState = mTempAlgorithmState;
@@ -107,7 +110,7 @@ public class StackScrollAlgorithm {
algorithmState.scrolledPixelsTop = 0;
algorithmState.itemsInBottomStack = 0.0f;
algorithmState.partialInBottom = 0.0f;
- algorithmState.scrollY = resultState.getScrollY() + mCollapsedSize;
+ algorithmState.scrollY = ambientState.getScrollY() + mCollapsedSize;
updateVisibleChildren(resultState, algorithmState);
@@ -120,19 +123,42 @@ public class StackScrollAlgorithm {
// Phase 3:
updateZValuesForState(resultState, algorithmState);
- handleDraggedViews(resultState, algorithmState);
+ handleDraggedViews(ambientState, resultState, algorithmState);
+ updateDimmedActivated(ambientState, resultState, algorithmState);
+ }
+
+ /**
+ * Updates the dimmed and activated states of the children.
+ */
+ private void updateDimmedActivated(AmbientState ambientState, StackScrollState resultState,
+ StackScrollAlgorithmState algorithmState) {
+ boolean dimmed = ambientState.isDimmed();
+ View activatedChild = ambientState.getActivatedChild();
+ int childCount = algorithmState.visibleChildren.size();
+ for (int i = 0; i < childCount; i++) {
+ View child = algorithmState.visibleChildren.get(i);
+ StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
+ childViewState.dimmed = dimmed;
+ childViewState.scale = !dimmed || activatedChild == child
+ ? 1.0f
+ : DIMMED_SCALE;
+ if (dimmed && activatedChild != null && child != activatedChild) {
+ childViewState.alpha *= ACTIVATED_INVERSE_ALPHA;
+ }
+ }
}
/**
* Handle the special state when views are being dragged
*/
- private void handleDraggedViews(StackScrollState resultState,
+ private void handleDraggedViews(AmbientState ambientState, StackScrollState resultState,
StackScrollAlgorithmState algorithmState) {
- for (View draggedView : mDraggedViews) {
+ ArrayList<View> draggedViews = ambientState.getDraggedViews();
+ for (View draggedView : draggedViews) {
int childIndex = algorithmState.visibleChildren.indexOf(draggedView);
if (childIndex >= 0 && childIndex < algorithmState.visibleChildren.size() - 1) {
View nextChild = algorithmState.visibleChildren.get(childIndex + 1);
- if (!mDraggedViews.contains(nextChild)) {
+ if (!draggedViews.contains(nextChild)) {
// only if the view is not dragged itself we modify its state to be fully
// visible
StackScrollState.ViewState viewState = resultState.getViewStateForView(
@@ -595,14 +621,6 @@ public class StackScrollAlgorithm {
}
}
- public void onBeginDrag(View view) {
- mDraggedViews.add(view);
- }
-
- public void onDragFinished(View view) {
- mDraggedViews.remove(view);
- }
-
class StackScrollAlgorithmState {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 70126f5..8fc26d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -37,19 +37,10 @@ public class StackScrollState {
private final ViewGroup mHostView;
private Map<ExpandableView, ViewState> mStateMap;
- private int mScrollY;
private final Rect mClipRect = new Rect();
private int mBackgroundRoundedRectCornerRadius;
private final Outline mChildOutline = new Outline();
- public int getScrollY() {
- return mScrollY;
- }
-
- public void setScrollY(int scrollY) {
- this.mScrollY = scrollY;
- }
-
public StackScrollState(ViewGroup hostView) {
mHostView = hostView;
mStateMap = new HashMap<ExpandableView, ViewState>();
@@ -106,10 +97,12 @@ public class StackScrollState {
float alpha = child.getAlpha();
float yTranslation = child.getTranslationY();
float zTranslation = child.getTranslationZ();
+ float scale = child.getScaleX();
int height = child.getActualHeight();
float newAlpha = state.alpha;
float newYTranslation = state.yTranslation;
float newZTranslation = state.zTranslation;
+ float newScale = state.scale;
int newHeight = state.height;
boolean becomesInvisible = newAlpha == 0.0f;
if (alpha != newAlpha) {
@@ -147,11 +140,20 @@ public class StackScrollState {
child.setTranslationZ(newZTranslation);
}
+ // apply scale
+ if (scale != newScale) {
+ child.setScaleX(newScale);
+ child.setScaleY(newScale);
+ }
+
// apply height
if (height != newHeight) {
- child.setActualHeight(newHeight);
+ child.setActualHeight(newHeight, false /* notifyListeners */);
}
+ // apply dimming
+ child.setDimmed(state.dimmed, false /* animate */);
+
// apply clipping and shadow
float newNotificationEnd = newYTranslation + newHeight;
@@ -228,6 +230,8 @@ public class StackScrollState {
float zTranslation;
int height;
boolean gone;
+ float scale;
+ boolean dimmed;
/**
* The location this view is currently rendered at.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 2e700aa..c952698 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -18,7 +18,9 @@ package com.android.systemui.statusbar.stack;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.view.View;
import android.view.animation.AnimationUtils;
@@ -37,14 +39,18 @@ import java.util.Stack;
*/
public class StackStateAnimator {
- private static final int ANIMATION_DURATION = 360;
+ public static final int ANIMATION_DURATION_STANDARD = 360;
+ public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
+
private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
+ private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag;
private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
+ private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag;
private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
@@ -58,6 +64,8 @@ public class StackStateAnimator {
private Set<Animator> mAnimatorSet = new HashSet<Animator>();
private Stack<AnimatorListenerAdapter> mAnimationListenerPool
= new Stack<AnimatorListenerAdapter>();
+ private AnimationFilter mAnimationFilter = new AnimationFilter();
+ private long mCurrentLength;
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
mHostLayout = hostLayout;
@@ -75,8 +83,9 @@ public class StackStateAnimator {
processAnimationEvents(mAnimationEvents, finalState);
- boolean hasNewEvents = !mNewEvents.isEmpty();
int childCount = mHostLayout.getChildCount();
+ mAnimationFilter.applyCombination(mNewEvents);
+ mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
for (int i = 0; i < childCount; i++) {
final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
@@ -84,7 +93,7 @@ public class StackStateAnimator {
continue;
}
- startAnimations(child, viewState, hasNewEvents);
+ startAnimations(child, viewState);
child.setClipBounds(null);
}
@@ -97,8 +106,7 @@ public class StackStateAnimator {
/**
* Start an animation to the given viewState
*/
- private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState,
- boolean hasNewEvents) {
+ private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState) {
int childVisibility = child.getVisibility();
boolean wasVisible = childVisibility == View.VISIBLE;
final float alpha = viewState.alpha;
@@ -107,33 +115,40 @@ public class StackStateAnimator {
}
// start translationY animation
if (child.getTranslationY() != viewState.yTranslation) {
- startYTranslationAnimation(child, viewState, hasNewEvents);
+ startYTranslationAnimation(child, viewState);
}
// start translationZ animation
if (child.getTranslationZ() != viewState.zTranslation) {
- startZTranslationAnimation(child, viewState, hasNewEvents);
+ startZTranslationAnimation(child, viewState);
+ }
+ // start scale animation
+ if (child.getScaleX() != viewState.scale) {
+ startScaleAnimation(child, viewState);
}
// start alpha animation
if (alpha != child.getAlpha()) {
- startAlphaAnimation(child, viewState, hasNewEvents);
+ startAlphaAnimation(child, viewState);
}
// start height animation
if (viewState.height != child.getActualHeight()) {
- startHeightAnimation(child, viewState, hasNewEvents);
+ startHeightAnimation(child, viewState);
}
+ // start dimmed animation
+ child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
}
private void startHeightAnimation(final ExpandableView child,
- StackScrollState.ViewState viewState, boolean hasNewEvents) {
- Integer previousEndValue = getChildTag(child,TAG_END_HEIGHT);
+ StackScrollState.ViewState viewState) {
+ Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
if (previousEndValue != null && previousEndValue == viewState.height) {
return;
}
ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
- long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
+ long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator,
+ mAnimationFilter.animateHeight);
if (newDuration <= 0) {
// no new animation needed, let's just apply the value
- child.setActualHeight(viewState.height);
+ child.setActualHeight(viewState.height, false /* notifyListeners */);
if (previousAnimator != null && !isRunning()) {
onAnimationFinished();
}
@@ -144,7 +159,8 @@ public class StackStateAnimator {
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- child.setActualHeight((int) animation.getAnimatedValue());
+ child.setActualHeight((int) animation.getAnimatedValue(),
+ false /* notifyListeners */);
}
});
animator.setInterpolator(mFastOutSlowInInterpolator);
@@ -164,14 +180,15 @@ public class StackStateAnimator {
}
private void startAlphaAnimation(final ExpandableView child,
- final StackScrollState.ViewState viewState, boolean hasNewEvents) {
+ final StackScrollState.ViewState viewState) {
final float endAlpha = viewState.alpha;
Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
if (previousEndValue != null && previousEndValue == endAlpha) {
return;
}
ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
- long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
+ long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator,
+ mAnimationFilter.animateAlpha);
if (newDuration <= 0) {
// no new animation needed, let's just apply the value
child.setAlpha(endAlpha);
@@ -228,13 +245,14 @@ public class StackStateAnimator {
}
private void startZTranslationAnimation(final ExpandableView child,
- final StackScrollState.ViewState viewState, boolean hasNewEvents) {
+ final StackScrollState.ViewState viewState) {
Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
if (previousEndValue != null && previousEndValue == viewState.zTranslation) {
return;
}
ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
- long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
+ long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator,
+ mAnimationFilter.animateZ);
if (newDuration <= 0) {
// no new animation needed, let's just apply the value
child.setTranslationZ(viewState.zTranslation);
@@ -264,13 +282,14 @@ public class StackStateAnimator {
}
private void startYTranslationAnimation(final ExpandableView child,
- StackScrollState.ViewState viewState, boolean hasNewEvents) {
+ StackScrollState.ViewState viewState) {
Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
if (previousEndValue != null && previousEndValue == viewState.yTranslation) {
return;
}
ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
- long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
+ long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator,
+ mAnimationFilter.animateY);
if (newDuration <= 0) {
// no new animation needed, let's just apply the value
child.setTranslationY(viewState.yTranslation);
@@ -298,6 +317,46 @@ public class StackStateAnimator {
child.setTag(TAG_END_TRANSLATION_Y, viewState.yTranslation);
}
+ private void startScaleAnimation(final ExpandableView child,
+ StackScrollState.ViewState viewState) {
+ Float previousEndValue = getChildTag(child, TAG_END_SCALE);
+ if (previousEndValue != null && previousEndValue == viewState.scale) {
+ return;
+ }
+ ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE);
+ long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator,
+ mAnimationFilter.animateScale);
+ if (newDuration <= 0) {
+ // no new animation needed, let's just apply the value
+ child.setScaleX(viewState.scale);
+ child.setScaleY(viewState.scale);
+ if (previousAnimator != null && !isRunning()) {
+ onAnimationFinished();
+ }
+ return;
+ }
+
+ PropertyValuesHolder holderX =
+ PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), viewState.scale);
+ PropertyValuesHolder holderY =
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), viewState.scale);
+ ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY);
+ animator.setInterpolator(mFastOutSlowInInterpolator);
+ animator.setDuration(newDuration);
+ animator.addListener(getGlobalAnimationFinishedListener());
+ // remove the tag when the animation is finished
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ child.setTag(TAG_ANIMATOR_SCALE, null);
+ child.setTag(TAG_END_SCALE, null);
+ }
+ });
+ startInstantly(animator);
+ child.setTag(TAG_ANIMATOR_SCALE, animator);
+ child.setTag(TAG_END_SCALE, viewState.scale);
+ }
+
/**
* Start an animator instantly instead of waiting on the next synchronization frame
*/
@@ -349,21 +408,22 @@ public class StackStateAnimator {
* Cancel the previous animator and get the duration of the new animation.
*
* @param previousAnimator the animator which was running before
- * @param hasNewEvents indicating whether new events came in in this animation
+ * @param newAnimationNeeded indicating whether a new animation should be started for this
+ * property
* @return the new duration
*/
private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator,
- boolean hasNewEvents) {
- long newDuration = ANIMATION_DURATION;
+ boolean newAnimationNeeded) {
+ long newDuration = mCurrentLength;
if (previousAnimator != null) {
- if (!hasNewEvents) {
+ if (!newAnimationNeeded) {
// This is only an update, no new event came in. lets just take the remaining
// duration as the new duration
newDuration = previousAnimator.getDuration()
- previousAnimator.getCurrentPlayTime();
}
previousAnimator.cancel();
- } else if (!hasNewEvents){
+ } else if (!newAnimationNeeded){
newDuration = 0;
}
return newDuration;
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 d6a8885..9006c9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -150,4 +150,11 @@ public class TvStatusBar extends BaseStatusBar {
protected void refreshLayout(int layoutDirection) {
}
+ @Override
+ public void onActivated(View view) {
+ }
+
+ @Override
+ public void onActivationReset(View view) {
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a09b8d2..29c781b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16523,7 +16523,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
- if (userId != 0) {
+ if (userId != UserHandle.USER_OWNER) {
Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntentLocked(null, null, intent, null,
@@ -16552,6 +16552,8 @@ public final class ActivityManagerService extends ActivityManagerNative
EventLogTags.writeAmSwitchUser(userId);
getUserManagerLocked().userForeground(userId);
sendUserSwitchBroadcastsLocked(oldUserId, userId);
+ } else {
+ mStackSupervisor.startBackgroundUserLocked(userId, uss);
}
if (needStart) {
@@ -16727,7 +16729,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
- void finishUserSwitch(UserStartedState uss) {
+ void finishUserBoot(UserStartedState uss) {
synchronized (this) {
if (uss.mState == UserStartedState.STATE_BOOTING
&& mStartedUsers.get(uss.mHandle.getIdentifier()) == uss) {
@@ -16741,6 +16743,12 @@ public final class ActivityManagerService extends ActivityManagerNative
android.Manifest.permission.RECEIVE_BOOT_COMPLETED, AppOpsManager.OP_NONE,
true, false, MY_PID, Process.SYSTEM_UID, userId);
}
+ }
+ }
+
+ void finishUserSwitch(UserStartedState uss) {
+ synchronized (this) {
+ finishUserBoot(uss);
startProfilesLocked();
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 7c3f288..efd2b57 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import android.os.Trace;
-import com.android.internal.R.styleable;
import com.android.internal.app.ResolverActivity;
import com.android.server.AttributeCache;
import com.android.server.am.ActivityStack.ActivityState;
@@ -480,7 +479,7 @@ final class ActivityRecord {
void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
if (task != null && task.removeActivity(this)) {
if (task != newTask) {
- task.stack.removeTask(task, false);
+ task.stack.removeTask(task);
} else {
Slog.d(TAG, "!!! REMOVE THIS LOG !!! setTask: nearly removed stack=" +
(newTask == null ? null : newTask.stack));
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ee39b67..a95710b 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -36,8 +36,6 @@ import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE;
import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
-import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.CONTAINER_STATE_HAS_SURFACE;
-
import android.service.voice.IVoiceInteractionSession;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.BatteryStatsImpl;
@@ -2841,7 +2839,7 @@ final class ActivityStack {
if (mStackSupervisor.isFrontStack(this) && task == topTask() && task.mOnTopOfHome) {
mStackSupervisor.moveHomeToTop();
}
- removeTask(task, false);
+ removeTask(task);
}
cleanUpActivityServicesLocked(r);
r.removeUriPermissionsLocked();
@@ -3717,7 +3715,7 @@ final class ActivityStack {
return starting;
}
- void removeTask(TaskRecord task, boolean moving) {
+ void removeTask(TaskRecord task) {
mStackSupervisor.endLockTaskModeIfTaskEnding(task);
mWindowManager.removeTask(task.taskId);
final ActivityRecord r = mResumedActivity;
@@ -3731,14 +3729,20 @@ final class ActivityStack {
mTaskHistory.get(taskNdx + 1).mOnTopOfHome = true;
}
mTaskHistory.remove(task);
- if (!moving && task.voiceSession != null) {
- // This task was a voice interaction, so it should not remain on the
- // recent tasks list.
- try {
- task.voiceSession.taskFinished(task.intent, task.taskId);
- } catch (RemoteException e) {
+
+ if (task.mActivities.isEmpty()) {
+ final boolean isVoiceSession = task.voiceSession != null;
+ if (isVoiceSession) {
+ try {
+ task.voiceSession.taskFinished(task.intent, task.taskId);
+ } catch (RemoteException e) {
+ }
+ }
+ if (task.autoRemoveFromRecents() || isVoiceSession) {
+ // Task creator asked to remove this when done, or this task was a voice
+ // interaction, so it should not remain on the recent tasks list.
+ mService.mRecentTasks.remove(task);
}
- mService.mRecentTasks.remove(task);
}
if (mTaskHistory.isEmpty()) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 2e979d2..00327ac 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -193,6 +193,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
/** Used on user changes */
final ArrayList<UserStartedState> mStartingUsers = new ArrayList<UserStartedState>();
+ /** Used to queue up any background users being started */
+ final ArrayList<UserStartedState> mStartingBackgroundUsers = new ArrayList<UserStartedState>();
+
/** Set to indicate whether to issue an onUserLeaving callback when a newly launched activity
* is being brought in front of us. */
boolean mUserLeaving = false;
@@ -1471,6 +1474,17 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
}
+ switch (r.info.documentLaunchMode) {
+ case ActivityInfo.DOCUMENT_LAUNCH_NONE:
+ break;
+ case ActivityInfo.DOCUMENT_LAUNCH_ALWAYS:
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ break;
+ case ActivityInfo.DOCUMENT_LAUNCH_INTO_EXISTING:
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+ break;
+ }
final boolean newDocument = intent.isDocument();
if (sourceRecord == null) {
// This activity is not being started from another... in this
@@ -1988,9 +2002,20 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (booting) {
mService.finishBooting();
- } else if (startingUsers != null) {
- for (int i = 0; i < startingUsers.size(); i++) {
- mService.finishUserSwitch(startingUsers.get(i));
+ } else {
+ // Complete user switch
+ if (startingUsers != null) {
+ for (int i = 0; i < startingUsers.size(); i++) {
+ mService.finishUserSwitch(startingUsers.get(i));
+ }
+ }
+ // Complete starting up of background users
+ if (mStartingBackgroundUsers.size() > 0) {
+ startingUsers = new ArrayList<UserStartedState>(mStartingBackgroundUsers);
+ mStartingBackgroundUsers.clear();
+ for (int i = 0; i < startingUsers.size(); i++) {
+ mService.finishUserBoot(startingUsers.get(i));
+ }
}
}
@@ -2237,7 +2262,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
Slog.w(TAG, "moveTaskToStack: no stack for id=" + stackId);
return;
}
- task.stack.removeTask(task, true);
+ task.stack.removeTask(task);
stack.addTask(task, toTop, true);
mWindowManager.addTask(taskId, stackId, toTop);
resumeTopActivitiesLocked();
@@ -2496,6 +2521,15 @@ public final class ActivityStackSupervisor implements DisplayListener {
return homeInFront;
}
+ /**
+ * Add background users to send boot completed events to.
+ * @param userId The user being started in the background
+ * @param uss The state object for the user.
+ */
+ public void startBackgroundUserLocked(int userId, UserStartedState uss) {
+ mStartingBackgroundUsers.add(uss);
+ }
+
final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) {
int N = mStoppingActivities.size();
if (N <= 0) return null;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 9f0bc10..862932c 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -139,6 +139,9 @@ final class TaskRecord extends ThumbnailHolder {
userId = UserHandle.getUserId(info.applicationInfo.uid);
creatorUid = info.applicationInfo.uid;
+ if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS);
+ }
}
void disposeThumbnail() {
@@ -246,6 +249,11 @@ final class TaskRecord extends ThumbnailHolder {
return mActivities.size() == 0;
}
+ boolean autoRemoveFromRecents() {
+ return intent != null &&
+ (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0;
+ }
+
/**
* Completely remove all activities associated with an existing
* task starting at a specified index.
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7aa5d79..fce86e8 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -198,8 +198,7 @@ public class NotificationManagerService extends SystemService {
private final UserProfiles mUserProfiles = new UserProfiles();
private NotificationListeners mListeners;
private ConditionProviders mConditionProviders;
-
- private final NotificationUsageStats mUsageStats = new NotificationUsageStats();
+ private NotificationUsageStats mUsageStats;
private static final String EXTRA_INTERCEPT = "android.intercept";
@@ -472,6 +471,7 @@ public class NotificationManagerService extends SystemService {
pw.println(prefix + String.format(" defaults=0x%08x flags=0x%08x",
notification.defaults, notification.flags));
pw.println(prefix + " sound=" + notification.sound);
+ pw.println(prefix + String.format(" color=0x%08x", notification.color));
pw.println(prefix + " vibrate=" + Arrays.toString(notification.vibrate));
pw.println(prefix + String.format(" led=0x%08x onMs=%d offMs=%d",
notification.ledARGB, notification.ledOnMS, notification.ledOffMS));
@@ -858,6 +858,7 @@ public class NotificationManagerService extends SystemService {
});
final File systemDir = new File(Environment.getDataDirectory(), "system");
mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
+ mUsageStats = new NotificationUsageStats(getContext());
importOldBlockDb();
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index d9e2b91..45ab3d3 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -18,8 +18,17 @@ package com.android.server.notification;
import com.android.server.notification.NotificationManagerService.NotificationRecord;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
+import android.util.Log;
import java.io.PrintWriter;
import java.util.HashMap;
@@ -37,9 +46,13 @@ import java.util.Map;
* {@hide}
*/
public class NotificationUsageStats {
-
// Guarded by synchronized(this).
private final Map<String, AggregatedStats> mStats = new HashMap<String, AggregatedStats>();
+ private final SQLiteLog mSQLiteLog;
+
+ public NotificationUsageStats(Context context) {
+ mSQLiteLog = new SQLiteLog(context);
+ }
/**
* Called when a notification has been posted.
@@ -49,6 +62,7 @@ public class NotificationUsageStats {
for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
stats.numPostedByApp++;
}
+ mSQLiteLog.logPosted(notification);
}
/**
@@ -68,6 +82,7 @@ public class NotificationUsageStats {
stats.numRemovedByApp++;
stats.collect(notification.stats);
}
+ mSQLiteLog.logRemoved(notification);
}
/**
@@ -79,6 +94,7 @@ public class NotificationUsageStats {
stats.numDismissedByUser++;
stats.collect(notification.stats);
}
+ mSQLiteLog.logDismissed(notification);
}
/**
@@ -89,6 +105,7 @@ public class NotificationUsageStats {
for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
stats.numClickedByUser++;
}
+ mSQLiteLog.logClicked(notification);
}
/**
@@ -146,6 +163,7 @@ public class NotificationUsageStats {
for (AggregatedStats as : mStats.values()) {
as.dump(pw, indent);
}
+ mSQLiteLog.dump(pw, indent);
}
/**
@@ -274,4 +292,211 @@ public class NotificationUsageStats {
'}';
}
}
+
+ private static class SQLiteLog {
+ private static final String TAG = "NotificationSQLiteLog";
+
+ // Message types passed to the background handler.
+ private static final int MSG_POST = 1;
+ private static final int MSG_CLICK = 2;
+ private static final int MSG_REMOVE = 3;
+ private static final int MSG_DISMISS = 4;
+
+ private static final String DB_NAME = "notification_log.db";
+ private static final int DB_VERSION = 1;
+
+ /** Age in ms after which events are pruned from the DB. */
+ private static final long HORIZON_MS = 7 * 24 * 60 * 60 * 1000L; // 1 week
+ /** Delay between pruning the DB. Used to throttle pruning. */
+ private static final long PRUNE_MIN_DELAY_MS = 6 * 60 * 60 * 1000L; // 6 hours
+ /** Mininum number of writes between pruning the DB. Used to throttle pruning. */
+ private static final long PRUNE_MIN_WRITES = 1024;
+
+ // Table 'log'
+ private static final String TAB_LOG = "log";
+ private static final String COL_EVENT_USER_ID = "event_user_id";
+ private static final String COL_EVENT_TYPE = "event_type";
+ private static final String COL_EVENT_TIME = "event_time_ms";
+ private static final String COL_KEY = "key";
+ private static final String COL_PKG = "pkg";
+ private static final String COL_NOTIFICATION_ID = "nid";
+ private static final String COL_TAG = "tag";
+ private static final String COL_WHEN_MS = "when_ms";
+ private static final String COL_DEFAULTS = "defaults";
+ private static final String COL_FLAGS = "flags";
+ private static final String COL_PRIORITY = "priority";
+ private static final String COL_CATEGORY = "category";
+ private static final String COL_ACTION_COUNT = "action_count";
+
+ private static final int EVENT_TYPE_POST = 1;
+ private static final int EVENT_TYPE_CLICK = 2;
+ private static final int EVENT_TYPE_REMOVE = 3;
+ private static final int EVENT_TYPE_DISMISS = 4;
+
+ private static long sLastPruneMs;
+ private static long sNumWrites;
+
+ private final SQLiteOpenHelper mHelper;
+ private final Handler mWriteHandler;
+
+ private static final long DAY_MS = 24 * 60 * 60 * 1000;
+
+ public SQLiteLog(Context context) {
+ HandlerThread backgroundThread = new HandlerThread("notification-sqlite-log",
+ android.os.Process.THREAD_PRIORITY_BACKGROUND);
+ backgroundThread.start();
+ mWriteHandler = new Handler(backgroundThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ NotificationRecord r = (NotificationRecord) msg.obj;
+ long nowMs = System.currentTimeMillis();
+ switch (msg.what) {
+ case MSG_POST:
+ writeEvent(r.sbn.getPostTime(), EVENT_TYPE_POST, r, true);
+ break;
+ case MSG_CLICK:
+ writeEvent(nowMs, EVENT_TYPE_CLICK, r, false);
+ break;
+ case MSG_REMOVE:
+ writeEvent(nowMs, EVENT_TYPE_REMOVE, r, false);
+ break;
+ case MSG_DISMISS:
+ writeEvent(nowMs, EVENT_TYPE_DISMISS, r, false);
+ break;
+ default:
+ Log.wtf(TAG, "Unknown message type: " + msg.what);
+ break;
+ }
+ }
+ };
+ mHelper = new SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TAB_LOG + " (" +
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
+ COL_EVENT_USER_ID + " INT," +
+ COL_EVENT_TYPE + " INT," +
+ COL_EVENT_TIME + " INT," +
+ COL_KEY + " TEXT," +
+ COL_PKG + " TEXT," +
+ COL_NOTIFICATION_ID + " INT," +
+ COL_TAG + " TEXT," +
+ COL_WHEN_MS + " INT," +
+ COL_DEFAULTS + " INT," +
+ COL_FLAGS + " INT," +
+ COL_PRIORITY + " INT," +
+ COL_CATEGORY + " TEXT," +
+ COL_ACTION_COUNT + " INT" +
+ ")");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ db.execSQL("DROP TABLE IF EXISTS " + TAB_LOG);
+ onCreate(db);
+ }
+ };
+ }
+
+ public void logPosted(NotificationRecord notification) {
+ mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_POST, notification));
+ }
+
+ public void logClicked(NotificationRecord notification) {
+ mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_CLICK, notification));
+ }
+
+ public void logRemoved(NotificationRecord notification) {
+ mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_REMOVE, notification));
+ }
+
+ public void logDismissed(NotificationRecord notification) {
+ mWriteHandler.sendMessage(mWriteHandler.obtainMessage(MSG_DISMISS, notification));
+ }
+
+ public void printPostFrequencies(PrintWriter pw, String indent) {
+ SQLiteDatabase db = mHelper.getReadableDatabase();
+ long nowMs = System.currentTimeMillis();
+ String q = "SELECT " +
+ COL_EVENT_USER_ID + ", " +
+ COL_PKG + ", " +
+ // Bucket by day by looking at 'floor((nowMs - eventTimeMs) / dayMs)'
+ "CAST(((" + nowMs + " - " + COL_EVENT_TIME + ") / " + DAY_MS + ") AS int) " +
+ "AS day, " +
+ "COUNT(*) AS cnt " +
+ "FROM " + TAB_LOG + " " +
+ "WHERE " +
+ COL_EVENT_TYPE + "=" + EVENT_TYPE_POST + " " +
+ "GROUP BY " + COL_EVENT_USER_ID + ", day, " + COL_PKG;
+ Cursor cursor = db.rawQuery(q, null);
+ try {
+ for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
+ int userId = cursor.getInt(0);
+ String pkg = cursor.getString(1);
+ int day = cursor.getInt(2);
+ int count = cursor.getInt(3);
+ pw.println(indent + "post_frequency{user_id=" + userId + ",pkg=" + pkg +
+ ",day=" + day + ",count=" + count + "}");
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void writeEvent(long eventTimeMs, int eventType, NotificationRecord r,
+ boolean populateNotificationDetails) {
+ ContentValues cv = new ContentValues();
+ cv.put(COL_EVENT_USER_ID, r.sbn.getUser().getIdentifier());
+ cv.put(COL_EVENT_TIME, eventTimeMs);
+ cv.put(COL_EVENT_TYPE, eventType);
+ putNotificationIdentifiers(r, cv);
+ if (populateNotificationDetails) {
+ putNotificationDetails(r, cv);
+ }
+ SQLiteDatabase db = mHelper.getWritableDatabase();
+ if (db.insert(TAB_LOG, null, cv) < 0) {
+ Log.wtf(TAG, "Error while trying to insert values: " + cv);
+ }
+ sNumWrites++;
+ pruneIfNecessary(db);
+ }
+
+ private void pruneIfNecessary(SQLiteDatabase db) {
+ // Prune if we haven't in a while.
+ long nowMs = System.currentTimeMillis();
+ if (sNumWrites > PRUNE_MIN_WRITES ||
+ nowMs - sLastPruneMs > PRUNE_MIN_DELAY_MS) {
+ sNumWrites = 0;
+ sLastPruneMs = nowMs;
+ long horizonStartMs = nowMs - HORIZON_MS;
+ int deletedRows = db.delete(TAB_LOG, COL_EVENT_TIME + " < ?",
+ new String[] { String.valueOf(horizonStartMs) });
+ Log.d(TAG, "Pruned event entries: " + deletedRows);
+ }
+ }
+
+ private static void putNotificationIdentifiers(NotificationRecord r, ContentValues outCv) {
+ outCv.put(COL_KEY, r.sbn.getKey());
+ outCv.put(COL_PKG, r.sbn.getPackageName());
+ }
+
+ private static void putNotificationDetails(NotificationRecord r, ContentValues outCv) {
+ outCv.put(COL_NOTIFICATION_ID, r.sbn.getId());
+ if (r.sbn.getTag() != null) {
+ outCv.put(COL_TAG, r.sbn.getTag());
+ }
+ outCv.put(COL_WHEN_MS, r.sbn.getPostTime());
+ outCv.put(COL_FLAGS, r.getNotification().flags);
+ outCv.put(COL_PRIORITY, r.getNotification().priority);
+ if (r.getNotification().category != null) {
+ outCv.put(COL_CATEGORY, r.getNotification().category);
+ }
+ outCv.put(COL_ACTION_COUNT, r.getNotification().actions != null ?
+ r.getNotification().actions.length : 0);
+ }
+
+ public void dump(PrintWriter pw, String indent) {
+ printPostFrequencies(pw, indent);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 48e9737..bd28e04 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -239,8 +239,10 @@ public class LauncherAppsService extends ILauncherApps.Stub {
private class MyPackageMonitor extends PackageMonitor {
- /** Checks if user is a profile of or same as listeningUser. */
- private boolean isProfileOf(UserHandle user, UserHandle listeningUser, String debugMsg) {
+ /** Checks if user is a profile of or same as listeningUser.
+ * and the user is enabled. */
+ private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
+ String debugMsg) {
if (user.getIdentifier() == listeningUser.getIdentifier()) {
if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
return true;
@@ -251,7 +253,8 @@ public class LauncherAppsService extends ILauncherApps.Stub {
UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
if (userInfo == null || listeningUserInfo == null
|| userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
- || userInfo.profileGroupId != listeningUserInfo.profileGroupId) {
+ || userInfo.profileGroupId != listeningUserInfo.profileGroupId
+ || !userInfo.isEnabled()) {
if (DEBUG) {
Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
+ debugMsg);
@@ -276,7 +279,7 @@ public class LauncherAppsService extends ILauncherApps.Stub {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isProfileOf(user, listeningUser, "onPackageAdded")) continue;
+ if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue;
try {
listener.onPackageAdded(user, packageName);
} catch (RemoteException re) {
@@ -295,7 +298,7 @@ public class LauncherAppsService extends ILauncherApps.Stub {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isProfileOf(user, listeningUser, "onPackageRemoved")) continue;
+ if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue;
try {
listener.onPackageRemoved(user, packageName);
} catch (RemoteException re) {
@@ -314,7 +317,7 @@ public class LauncherAppsService extends ILauncherApps.Stub {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isProfileOf(user, listeningUser, "onPackageModified")) continue;
+ if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue;
try {
listener.onPackageChanged(user, packageName);
} catch (RemoteException re) {
@@ -333,7 +336,7 @@ public class LauncherAppsService extends ILauncherApps.Stub {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
+ if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
try {
listener.onPackagesAvailable(user, packages, isReplacing());
} catch (RemoteException re) {
@@ -352,7 +355,7 @@ public class LauncherAppsService extends ILauncherApps.Stub {
for (int i = 0; i < n; i++) {
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
- if (!isProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
+ if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
try {
listener.onPackagesUnavailable(user, packages, isReplacing());
} catch (RemoteException re) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4318b0e..e746c1a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -299,6 +299,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
boolean mHasSurface = false;
+ boolean mNotOnAppsDisplay = false;
DisplayContent mDisplayContent;
/** When true this window can be displayed on screens owther than mOwnerUid's */
@@ -430,6 +431,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
mRootToken = appToken;
mAppToken = appToken.appWindowToken;
+ if (mAppToken != null) {
+ final DisplayContent appDisplay = getDisplayContent();
+ mNotOnAppsDisplay = displayContent != appDisplay;
+ }
mWinAnimator = new WindowStateAnimator(this);
mWinAnimator.mAlpha = a.alpha;
@@ -717,7 +722,8 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
public DisplayContent getDisplayContent() {
- return mAppToken == null ? mDisplayContent : getStack().getDisplayContent();
+ return mAppToken == null || mNotOnAppsDisplay ?
+ mDisplayContent : getStack().getDisplayContent();
}
public int getDisplayId() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index dcca837..9a9f1c8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -100,6 +100,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -232,6 +233,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
static class ActiveAdmin {
private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
private static final String TAG_DISABLE_CAMERA = "disable-camera";
+ private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
+ private static final String TAG_ACCOUNT_TYPE = "account-type";
private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
private static final String TAG_PASSWORD_EXPIRATION_DATE = "password-expiration-date";
private static final String TAG_PASSWORD_EXPIRATION_TIMEOUT = "password-expiration-timeout";
@@ -297,6 +300,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
boolean encryptionRequested = false;
boolean disableCamera = false;
+ Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
// TODO: review implementation decisions with frameworks team
boolean specifiesGlobalProxy = false;
@@ -413,6 +417,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
out.endTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
}
+ if (!accountTypesWithManagementDisabled.isEmpty()) {
+ out.startTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT);
+ for (String ac : accountTypesWithManagementDisabled) {
+ out.startTag(null, TAG_ACCOUNT_TYPE);
+ out.attribute(null, ATTR_VALUE, ac);
+ out.endTag(null, TAG_ACCOUNT_TYPE);
+ }
+ out.endTag(null, TAG_DISABLE_ACCOUNT_MANAGEMENT);
+ }
}
void readFromXml(XmlPullParser parser)
@@ -484,6 +497,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
disabledKeyguardFeatures = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_DISABLE_ACCOUNT_MANAGEMENT.equals(tag)) {
+ int outerDepthDAM = parser.getDepth();
+ int typeDAM;
+ while ((typeDAM=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (typeDAM != XmlPullParser.END_TAG
+ || parser.getDepth() > outerDepthDAM)) {
+ if (typeDAM == XmlPullParser.END_TAG || typeDAM == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagDAM = parser.getName();
+ if (TAG_ACCOUNT_TYPE.equals(tagDAM)) {
+ accountTypesWithManagementDisabled.add(
+ parser.getAttributeValue(null, ATTR_VALUE));
+ } else {
+ Slog.w(LOG_TAG, "Unknown tag under " + tag + ": " + tagDAM);
+ }
+ }
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
}
@@ -3178,7 +3208,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (who == null) {
throw new NullPointerException("ComponentName is null");
}
-
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
int userId = UserHandle.getCallingUserId();
@@ -3277,4 +3306,42 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0, userId);
return (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0;
}
+
+ @Override
+ public void setAccountManagementDisabled(ComponentName who, String accountType,
+ boolean disabled) {
+ if (!mHasFeature) {
+ return;
+ }
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (disabled) {
+ ap.accountTypesWithManagementDisabled.add(accountType);
+ } else {
+ ap.accountTypesWithManagementDisabled.remove(accountType);
+ }
+ saveSettingsLocked(UserHandle.getCallingUserId());
+ }
+ }
+
+ @Override
+ public String[] getAccountTypesWithManagementDisabled() {
+ if (!mHasFeature) {
+ return null;
+ }
+ synchronized (this) {
+ DevicePolicyData policy = getUserData(UserHandle.getCallingUserId());
+ final int N = policy.mAdminList.size();
+ HashSet<String> resultSet = new HashSet<String>();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ resultSet.addAll(admin.accountTypesWithManagementDisabled);
+ }
+ return resultSet.toArray(new String[resultSet.size()]);
+ }
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 722b797..716823c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -605,6 +605,14 @@ public final class SystemServer {
if (!disableNetwork) {
try {
+ Slog.i(TAG, "Network Score Service");
+ networkScore = new NetworkScoreService(context);
+ ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
+ } catch (Throwable e) {
+ reportWtf("starting Network Score Service", e);
+ }
+
+ try {
Slog.i(TAG, "NetworkStats Service");
networkStats = new NetworkStatsService(context, networkManagement, alarm);
ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
@@ -642,6 +650,15 @@ public final class SystemServer {
}
try {
+ Slog.i(TAG, "Wi-Fi Scanning Service");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.WifiScanningService");
+
+ } catch (Throwable e) {
+ reportWtf("starting Wi-Fi Scanning Service", e);
+ }
+
+ try {
Slog.i(TAG, "Connectivity Service");
connectivity = new ConnectivityService(
context, networkManagement, networkStats, networkPolicy);
@@ -653,14 +670,6 @@ public final class SystemServer {
}
try {
- Slog.i(TAG, "Network Score Service");
- networkScore = new NetworkScoreService(context);
- ServiceManager.addService(Context.NETWORK_SCORE_SERVICE, networkScore);
- } catch (Throwable e) {
- reportWtf("starting Network Score Service", e);
- }
-
- try {
Slog.i(TAG, "Network Service Discovery Service");
serviceDiscovery = NsdService.create(context);
ServiceManager.addService(
diff --git a/core/res/res/drawable/stat_sys_adb.xml b/wifi/java/android/net/wifi/IWifiScanner.aidl
index dfc8563..fef2d11 100644
--- a/core/res/res/drawable/stat_sys_adb.xml
+++ b/wifi/java/android/net/wifi/IWifiScanner.aidl
@@ -1,13 +1,11 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
/*
- * Copyright 2013, The Android Open Source Project
+ * Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -15,9 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--->
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/stat_sys_adb_am"
- android:autoMirrored="true">
-</bitmap>
+package android.net.wifi;
+
+import android.os.Messenger;
+
+/**
+ * {@hide}
+ */
+interface IWifiScanner
+{
+ Messenger getMessenger();
+}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index d7ecaff..1cb9546 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -56,6 +56,13 @@ public class ScanResult implements Parcelable {
public long timestamp;
/**
+ * Timestamp representing date when this result was last seen, in milliseconds from 1970
+ * {@hide}
+ */
+ public long seen;
+
+
+ /**
* The approximate distance to the AP in centimeter, if available. Else
* {@link UNSPECIFIED}.
* {@hide}
@@ -114,9 +121,17 @@ public class ScanResult implements Parcelable {
timestamp = source.timestamp;
distanceCm = source.distanceCm;
distanceSdCm = source.distanceSdCm;
+ seen = source.seen;
}
}
+ /** empty scan result
+ *
+ * {@hide}
+ * */
+ public ScanResult() {
+ }
+
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 6562462..ce8c8b8 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -21,6 +21,7 @@ import android.os.Parcelable;
import android.os.Parcel;
import android.text.TextUtils;
+import java.util.HashMap;
import java.util.BitSet;
/**
@@ -302,6 +303,156 @@ public class WifiConfiguration implements Parcelable {
/**
* @hide
+ * dhcp server MAC address if known
+ */
+ public String dhcpServer;
+
+ /**
+ * @hide
+ * default Gateway MAC address if known
+ */
+ public String defaultGwMacAddress;
+
+ /**
+ * @hide
+ * BSSID list on which this configuration was seen.
+ * TODO: prevent this list to grow infinitely, age-out the results
+ */
+ public HashMap<String, ScanResult> scanResultCache;
+
+ /** @hide **/
+ public static int INVALID_RSSI = -127;
+
+ /**
+ * @hide
+ * A summary of the RSSI and Band status for that configuration
+ * This is used as a temporary value by the auto-join controller
+ */
+ public final class Visibility
+ {
+ public int rssi5; // strongest 5GHz RSSI
+ public int rssi24; // strongest 2.4GHz RSSI
+ public int num5; // number of BSSIDs on 5GHz
+ public int num24; // number of BSSIDs on 2.4GHz
+ public long age5; // timestamp of the strongest 5GHz BSSID (last time it was seen)
+ public long age24; // timestamp of the strongest 2.4GHz BSSID (last time it was seen)
+ public Visibility()
+ {
+ rssi5 = INVALID_RSSI;
+ rssi24 = INVALID_RSSI;
+ }
+ public Visibility(Visibility source)
+ {
+ rssi5 = source.rssi5;
+ rssi24 = source.rssi24;
+ age24 = source.age24;
+ age5 = source.age5;
+ num24 = source.num24;
+ num5 = source.num5;
+ }
+ }
+
+ /** @hide
+ * Cache the visibility status of this configuration.
+ * Visibility can change at any time depending on scan results availability.
+ * Owner of the WifiConfiguration is responsible to set this field based on
+ * recent scan results.
+ ***/
+ public Visibility visibility;
+
+ /** @hide
+ * calculate and set Visibility for that configuration.
+ *
+ * age in milliseconds: we will consider only ScanResults that are more recent,
+ * i.e. younger.
+ ***/
+ public Visibility setVisibility(long age) {
+ if (scanResultCache == null) {
+ visibility = null;
+ return null;
+ }
+
+ Visibility status = new Visibility();
+
+ long now_ms = System.currentTimeMillis();
+ for(ScanResult result : scanResultCache.values()) {
+ if (result.seen == 0)
+ continue;
+
+ if ((result.frequency > 4900) && (result.frequency < 5900)) {
+ //strictly speaking: [4915, 5825]
+ //number of known BSSID on 5GHz band
+ status.num5 = status.num5 + 1;
+ } else if ((result.frequency > 2400) && (result.frequency < 2500)) {
+ //strictly speaking: [2412, 2482]
+ //number of known BSSID on 2.4Ghz band
+ status.num24 = status.num24 + 1;
+ }
+
+ if ((now_ms - result.seen) > age) continue;
+
+ if ((result.frequency > 4900) && (result.frequency < 5900)) {
+ if (result.level > status.rssi5) {
+ status.rssi5 = result.level;
+ status.age5 = result.seen;
+ }
+ } else if ((result.frequency > 2400) && (result.frequency < 2500)) {
+ if (result.level > status.rssi24) {
+ status.rssi24 = result.level;
+ status.age24 = result.seen;
+ }
+ }
+ }
+ visibility = status;
+ return status;
+ }
+
+ /** @hide */
+ public static final int AUTO_JOIN_ENABLED = 0;
+ /** @hide */
+ public static final int AUTO_JOIN_DISABLED_ON_AUTH_FAILURE = 1;
+ /**
+ * @hide
+ */
+ public int autoJoinStatus;
+
+ /**
+ * @hide
+ * Indicate that a WifiConfiguration is temporary and should not be saved
+ * nor considered by AutoJoin.
+ */
+ public boolean ephemeral;
+
+ /**
+ * @hide
+ * Connect choices
+ *
+ * remember the keys identifying the known WifiConfiguration over which this configuration
+ * was preferred by user or a "WiFi Network Management app", that is,
+ * a WifiManager.CONNECT_NETWORK or SELECT_NETWORK was received while this configuration
+ * was visible to the user:
+ * configKey is : "SSID"-WEP-WPA_PSK-WPA_EAP
+ *
+ * The integer represents the configuration's RSSI at that time (useful?)
+ *
+ * The overall auto-join algorithm make use of past connect choice so as to sort configuration,
+ * the exact algorithm still fluctuating as of 5/7/2014
+ *
+ */
+ public HashMap<String, Integer> connectChoices;
+
+ /**
+ * @hide
+ * Linked Configurations: represent the set of Wificonfigurations that are equivalent
+ * regarding roaming and auto-joining.
+ * The linked configuration may or may not have same SSID, and may or may not have same
+ * credentials.
+ * For instance, linked configurations will have same defaultGwMacAddress or same dhcp server.
+ */
+ public HashMap<String, Integer> linkedConfigurations;
+
+ /**
+ * @hide
*/
public enum ProxySettings {
/* No proxy is to be used. Any existing proxy settings
@@ -346,6 +497,7 @@ public class WifiConfiguration implements Parcelable {
ipAssignment = IpAssignment.UNASSIGNED;
proxySettings = ProxySettings.UNASSIGNED;
linkProperties = new LinkProperties();
+ autoJoinStatus = AUTO_JOIN_ENABLED;
}
/**
@@ -369,6 +521,32 @@ public class WifiConfiguration implements Parcelable {
// TODO: Add more checks
return true;
+
+ }
+
+ /**
+ * most recent time we have seen this configuration
+ * @return most recent scanResult
+ * @hide
+ */
+ public ScanResult lastSeen() {
+ ScanResult mostRecent = null;
+
+ if (scanResultCache == null) {
+ return null;
+ }
+
+ for (ScanResult result : scanResultCache.values()) {
+ if (mostRecent == null) {
+ if (result.seen != 0)
+ mostRecent = result;
+ } else {
+ if (result.seen > mostRecent.seen) {
+ mostRecent = result;
+ }
+ }
+ }
+ return mostRecent;
}
@Override
@@ -570,7 +748,48 @@ public class WifiConfiguration implements Parcelable {
return KeyMgmt.NONE;
}
- /** Implement the Parcelable interface {@hide} */
+ /* @hide
+ * Cache the config key, this seems useful as a speed up since a lot of
+ * lookups in the config store are done and based on this key.
+ */
+ String mCachedConfigKey;
+
+ /** @hide
+ * return the string used to calculate the hash in WifiConfigStore
+ * and uniquely identify this WifiConfiguration
+ */
+ public String configKey(boolean allowCached) {
+ String key;
+ if (allowCached && mCachedConfigKey != null) {
+ key = mCachedConfigKey;
+ } else {
+ key = this.SSID;
+ if (key == null)
+ key = "";
+ if (this.wepKeys[0] != null) {
+ key = key + "-WEP";
+ }
+ if (this.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+ key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_PSK];
+ }
+ if (this.allowedKeyManagement.get(KeyMgmt.WPA_EAP) ||
+ this.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
+ key = key + "-" + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+ }
+ mCachedConfigKey = key;
+ }
+ return key;
+ }
+
+ /** @hide
+ * get configKey, force calculating the config string
+ */
+ public String configKey() {
+ return configKey(false);
+ }
+
+
+ /** Implement the Parcelable interface {@hide} */
public int describeContents() {
return 0;
}
@@ -603,8 +822,32 @@ public class WifiConfiguration implements Parcelable {
ipAssignment = source.ipAssignment;
proxySettings = source.proxySettings;
+
+ defaultGwMacAddress = source.defaultGwMacAddress;
+
linkProperties = new LinkProperties(source.linkProperties);
- }
+ if ((source.scanResultCache != null) && (source.scanResultCache.size() > 0)) {
+ scanResultCache = new HashMap<String, ScanResult>();
+ scanResultCache.putAll(source.scanResultCache);
+ }
+
+ if ((source.connectChoices != null) && (source.connectChoices.size() > 0)) {
+ connectChoices = new HashMap<String, Integer>();
+ connectChoices.putAll(source.connectChoices);
+ }
+
+ if ((source.linkedConfigurations != null)
+ && (source.linkedConfigurations.size() > 0)) {
+ linkedConfigurations = new HashMap<String, Integer>();
+ linkedConfigurations.putAll(source.linkedConfigurations);
+ }
+ mCachedConfigKey = null; //force null configKey
+ autoJoinStatus = source.autoJoinStatus;
+
+ if (source.visibility != null) {
+ visibility = new Visibility(source.visibility);
+ }
+ }
}
/** Implement the Parcelable interface {@hide} */
@@ -633,6 +876,10 @@ public class WifiConfiguration implements Parcelable {
dest.writeString(ipAssignment.name());
dest.writeString(proxySettings.name());
dest.writeParcelable(linkProperties, flags);
+
+ dest.writeString(dhcpServer);
+ dest.writeString(defaultGwMacAddress);
+ dest.writeInt(autoJoinStatus);
}
/** Implement the Parcelable interface {@hide} */
@@ -664,6 +911,10 @@ public class WifiConfiguration implements Parcelable {
config.proxySettings = ProxySettings.valueOf(in.readString());
config.linkProperties = in.readParcelable(null);
+ config.dhcpServer = in.readString();
+ config.defaultGwMacAddress = in.readString();
+ config.autoJoinStatus = in.readInt();
+
return config;
}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
new file mode 100644
index 0000000..e02e14c
--- /dev/null
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+
+/**
+ * This class provides a way to scan the Wifi universe around the device
+ * Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(String) Context.getSystemService(Context
+ * .WIFI_SCANNING_SERVICE)}.
+ * @hide
+ */
+public class WifiScanner {
+
+ public static final int WIFI_BAND_UNSPECIFIED = 0; /* not specified */
+ public static final int WIFI_BAND_24_GHZ = 1; /* 2.4 GHz band */
+ public static final int WIFI_BAND_5_GHZ = 2; /* 5 GHz band without DFS channels */
+ public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; /* 5 GHz band with DFS channels */
+ public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; /* 5 GHz band with DFS channels */
+ public static final int WIFI_BAND_BOTH = 3; /* both bands without DFS channels */
+ public static final int WIFI_BAND_BOTH_WITH_DFS = 7; /* both bands with DFS channels */
+
+ public static final int MIN_SCAN_PERIOD_MS = 300; /* minimum supported period */
+ public static final int MAX_SCAN_PERIOD_MS = 1024000; /* maximum supported period */
+
+ public static final int REASON_SUCCEEDED = 0;
+ public static final int REASON_UNSPECIFIED = -1;
+ public static final int REASON_INVALID_LISTENER = -2;
+ public static final int REASON_INVALID_REQUEST = -3;
+ public static final int REASON_CONFLICTING_REQUEST = -4;
+
+ public static interface ActionListener {
+ public void onSuccess(Object result);
+ public void onFailure(int reason, Object exception);
+ }
+
+ /**
+ * gives you all the possible channels; channel is specified as an
+ * integer with frequency in MHz i.e. channel 1 is 2412
+ */
+ public List<Integer> getAvailableChannels(int band) {
+ return null;
+ }
+
+ /**
+ * provides channel specification to the APIs
+ */
+ public static class ChannelSpec {
+ public int frequency;
+ public boolean passive; /* ignored on DFS channels */
+ public int dwellTimeMS; /* not supported for now */
+
+ public ChannelSpec(int frequency) {
+ this.frequency = frequency;
+ passive = false;
+ dwellTimeMS = 0;
+ }
+ }
+
+ public static final int REPORT_EVENT_AFTER_BUFFER_FULL = 0;
+ public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1;
+ public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2;
+
+ /**
+ * scan configuration parameters
+ */
+ public static class ScanSettings implements Parcelable {
+
+ public int band; /* ignore channels if specified */
+ public ChannelSpec[] channels; /* list of channels to scan */
+ public int periodInMs; /* period of scan */
+ public int reportEvents; /* a valid REPORT_EVENT value */
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(band);
+ dest.writeInt(periodInMs);
+ dest.writeInt(channels.length);
+
+ for (int i = 0; i < channels.length; i++) {
+ dest.writeInt(channels[i].frequency);
+ dest.writeInt(channels[i].dwellTimeMS);
+ dest.writeInt(channels[i].passive ? 1 : 0);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<ScanSettings> CREATOR =
+ new Creator<ScanSettings>() {
+ public ScanSettings createFromParcel(Parcel in) {
+
+ ScanSettings settings = new ScanSettings();
+ settings.band = in.readInt();
+ settings.periodInMs = in.readInt();
+ int num_channels = in.readInt();
+ settings.channels = new ChannelSpec[num_channels];
+ for (int i = 0; i < num_channels; i++) {
+ int frequency = in.readInt();
+
+ ChannelSpec spec = new ChannelSpec(frequency);
+ spec.dwellTimeMS = in.readInt();
+ spec.passive = in.readInt() == 1;
+ settings.channels[i] = spec;
+ }
+
+ return settings;
+ }
+
+ public ScanSettings[] newArray(int size) {
+ return new ScanSettings[size];
+ }
+ };
+
+ }
+
+ public static class InformationElement {
+ public int id;
+ public byte[] bytes;
+ }
+
+ public static class FullScanResult {
+ public ScanResult result;
+ public InformationElement informationElements[];
+ }
+
+ /** @hide */
+ public static class ParcelableScanResults implements Parcelable {
+ public ScanResult mResults[];
+
+ public ParcelableScanResults(ScanResult[] results) {
+ mResults = results;
+ }
+
+ public ScanResult[] getResults() {
+ return mResults;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mResults.length);
+ for (int i = 0; i < mResults.length; i++) {
+ ScanResult result = mResults[i];
+ result.writeToParcel(dest, flags);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<ParcelableScanResults> CREATOR =
+ new Creator<ParcelableScanResults>() {
+ public ParcelableScanResults createFromParcel(Parcel in) {
+ int n = in.readInt();
+ ScanResult results[] = new ScanResult[n];
+ for (int i = 0; i < n; i++) {
+ results[i] = ScanResult.CREATOR.createFromParcel(in);
+ }
+ return new ParcelableScanResults(results);
+ }
+
+ public ParcelableScanResults[] newArray(int size) {
+ return new ParcelableScanResults[size];
+ }
+ };
+ }
+
+ /**
+ * Framework is co-ordinating scans across multiple apps; so it may not give exactly the
+ * same period requested. The period granted is stated on the onSuccess() event; and
+ * onPeriodChanged() will be called if/when it is changed because of multiple conflicting
+ * requests. This is similar to the way timers are handled.
+ */
+ public interface ScanListener extends ActionListener {
+ public void onPeriodChanged(int periodInMs);
+ public void onResults(ScanResult[] results);
+ public void onFullResult(FullScanResult fullScanResult);
+ }
+
+ public void scan(ScanSettings settings, ScanListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_SCAN, 0, putListener(listener), settings);
+ }
+ public void startBackgroundScan(ScanSettings settings, ScanListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_START_BACKGROUND_SCAN, 0, putListener(listener), settings);
+ }
+ public void stopBackgroundScan(boolean flush, ScanListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, removeListener(listener));
+ }
+ public void retrieveScanResults(boolean flush, ScanListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_GET_SCAN_RESULTS, 0, getListenerKey(listener));
+ }
+
+ public static class HotspotInfo {
+ public String bssid;
+ public int low; /* minimum RSSI */
+ public int high; /* maximum RSSI */
+ }
+
+ public static class WifiChangeSettings {
+ public int rssiSampleSize; /* sample size for RSSI averaging */
+ public int lostApSampleSize; /* samples to confirm AP's loss */
+ public int unchangedSampleSize; /* samples to confirm no change */
+ public int minApsBreachingThreshold; /* change threshold to trigger event */
+ public HotspotInfo[] hotspotInfos;
+ }
+
+ /* overrides the significant wifi change state machine configuration */
+ public void configureSignificantWifiChange(
+ int rssiSampleSize, /* sample size for RSSI averaging */
+ int lostApSampleSize, /* samples to confirm AP's loss */
+ int unchangedSampleSize, /* samples to confirm no change */
+ int minApsBreachingThreshold, /* change threshold to trigger event */
+ HotspotInfo[] hotspotInfos /* signal thresholds to crosss */
+ )
+ {
+ validateChannel();
+ WifiChangeSettings settings = new WifiChangeSettings();
+ settings.rssiSampleSize = rssiSampleSize;
+ settings.lostApSampleSize = lostApSampleSize;
+ settings.unchangedSampleSize = unchangedSampleSize;
+ settings.minApsBreachingThreshold = minApsBreachingThreshold;
+ settings.hotspotInfos = hotspotInfos;
+
+ sAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings);
+ }
+
+ public interface SignificantWifiChangeListener extends ActionListener {
+ public void onChanging(ScanResult[] results); /* changes are found */
+ public void onQuiescence(ScanResult[] results); /* changes settled down */
+ }
+
+ public void trackSignificantWifiChange(SignificantWifiChangeListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_START_TRACKING_CHANGE, 0, putListener(listener));
+ }
+ public void untrackSignificantWifiChange(SignificantWifiChangeListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_STOP_TRACKING_CHANGE, 0, removeListener(listener));
+ }
+
+ public void configureSignificantWifiChange(WifiChangeSettings settings) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_CONFIGURE_WIFI_CHANGE, 0, 0, settings);
+ }
+
+ public static interface HotlistListener extends ActionListener {
+ public void onFound(ScanResult[] results);
+ }
+
+ /** @hide */
+ public static class HotlistSettings implements Parcelable {
+ public HotspotInfo[] hotspotInfos;
+ public int apLostThreshold;
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(apLostThreshold);
+ dest.writeInt(hotspotInfos.length);
+ for (int i = 0; i < hotspotInfos.length; i++) {
+ HotspotInfo info = hotspotInfos[i];
+ dest.writeString(info.bssid);
+ dest.writeInt(info.low);
+ dest.writeInt(info.high);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<HotlistSettings> CREATOR =
+ new Creator<HotlistSettings>() {
+ public HotlistSettings createFromParcel(Parcel in) {
+ HotlistSettings settings = new HotlistSettings();
+ settings.apLostThreshold = in.readInt();
+ int n = in.readInt();
+ settings.hotspotInfos = new HotspotInfo[n];
+ for (int i = 0; i < n; i++) {
+ HotspotInfo info = new HotspotInfo();
+ info.bssid = in.readString();
+ info.low = in.readInt();
+ info.high = in.readInt();
+ settings.hotspotInfos[i] = info;
+ }
+ return settings;
+ }
+
+ public HotlistSettings[] newArray(int size) {
+ return new HotlistSettings[size];
+ }
+ };
+ }
+
+ public void setHotlist(HotspotInfo[] hotspots,
+ int apLostThreshold, HotlistListener listener) {
+ validateChannel();
+ HotlistSettings settings = new HotlistSettings();
+ settings.hotspotInfos = hotspots;
+ sAsyncChannel.sendMessage(CMD_SET_HOTLIST, 0, putListener(listener), settings);
+ }
+
+ public void resetHotlist(HotlistListener listener) {
+ validateChannel();
+ sAsyncChannel.sendMessage(CMD_RESET_HOTLIST, 0, removeListener(listener));
+ }
+
+
+ /* private members and methods */
+
+ private static final String TAG = "WifiScanner";
+ private static final boolean DBG = true;
+
+ /* commands for Wifi Service */
+ private static final int BASE = Protocol.BASE_WIFI_SCANNER;
+
+ /** @hide */
+ public static final int CMD_SCAN = BASE + 0;
+ /** @hide */
+ public static final int CMD_START_BACKGROUND_SCAN = BASE + 2;
+ /** @hide */
+ public static final int CMD_STOP_BACKGROUND_SCAN = BASE + 3;
+ /** @hide */
+ public static final int CMD_GET_SCAN_RESULTS = BASE + 4;
+ /** @hide */
+ public static final int CMD_SCAN_RESULT = BASE + 5;
+ /** @hide */
+ public static final int CMD_SET_HOTLIST = BASE + 6;
+ /** @hide */
+ public static final int CMD_RESET_HOTLIST = BASE + 7;
+ /** @hide */
+ public static final int CMD_AP_FOUND = BASE + 9;
+ /** @hide */
+ public static final int CMD_AP_LOST = BASE + 10;
+ /** @hide */
+ public static final int CMD_START_TRACKING_CHANGE = BASE + 11;
+ /** @hide */
+ public static final int CMD_STOP_TRACKING_CHANGE = BASE + 12;
+ /** @hide */
+ public static final int CMD_CONFIGURE_WIFI_CHANGE = BASE + 13;
+ /** @hide */
+ public static final int CMD_WIFI_CHANGE_DETECTED = BASE + 15;
+ /** @hide */
+ public static final int CMD_WIFI_CHANGES_STABILIZED = BASE + 16;
+ /** @hide */
+ public static final int CMD_OP_SUCCEEDED = BASE + 17;
+ /** @hide */
+ public static final int CMD_OP_FAILED = BASE + 18;
+ /** @hide */
+ public static final int CMD_PERIOD_CHANGED = BASE + 19;
+ /** @hide */
+ public static final int CMD_FULL_SCAN_RESULT = BASE + 20;
+
+ private Context mContext;
+ private IWifiScanner mService;
+
+ private static final int INVALID_KEY = 0;
+ private static int sListenerKey = 1;
+
+ private static final SparseArray sListenerMap = new SparseArray();
+ private static final Object sListenerMapLock = new Object();
+
+ private static AsyncChannel sAsyncChannel;
+ private static CountDownLatch sConnected;
+
+ private static final Object sThreadRefLock = new Object();
+ private static int sThreadRefCount;
+ private static HandlerThread sHandlerThread;
+
+ /**
+ * Create a new WifiScanner instance.
+ * Applications will almost always want to use
+ * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
+ * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
+ * @param context the application context
+ * @param service the Binder interface
+ * @hide
+ */
+ public WifiScanner(Context context, IWifiScanner service) {
+ mContext = context;
+ mService = service;
+ init();
+ }
+
+ private void init() {
+ synchronized (sThreadRefLock) {
+ if (++sThreadRefCount == 1) {
+ Messenger messenger = null;
+ try {
+ messenger = mService.getMessenger();
+ } catch (RemoteException e) {
+ /* do nothing */
+ } catch (SecurityException e) {
+ /* do nothing */
+ }
+
+ if (messenger == null) {
+ sAsyncChannel = null;
+ return;
+ }
+
+ sHandlerThread = new HandlerThread("WifiScanner");
+ sAsyncChannel = new AsyncChannel();
+ sConnected = new CountDownLatch(1);
+
+ sHandlerThread.start();
+ Handler handler = new ServiceHandler(sHandlerThread.getLooper());
+ sAsyncChannel.connect(mContext, handler, messenger);
+ try {
+ sConnected.await();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "interrupted wait at init");
+ }
+ }
+ }
+ }
+
+ private void validateChannel() {
+ if (sAsyncChannel == null) throw new IllegalStateException(
+ "No permission to access and change wifi or a bad initialization");
+ }
+
+ private static int putListener(Object listener) {
+ if (listener == null) return INVALID_KEY;
+ int key;
+ synchronized (sListenerMapLock) {
+ do {
+ key = sListenerKey++;
+ } while (key == INVALID_KEY);
+ sListenerMap.put(key, listener);
+ }
+ return key;
+ }
+
+ private static Object getListener(int key) {
+ if (key == INVALID_KEY) return null;
+ synchronized (sListenerMapLock) {
+ Object listener = sListenerMap.get(key);
+ return listener;
+ }
+ }
+
+ private static int getListenerKey(Object listener) {
+ if (listener == null) return INVALID_KEY;
+ synchronized (sListenerMapLock) {
+ int index = sListenerMap.indexOfValue(listener);
+ if (index == -1) {
+ return INVALID_KEY;
+ } else {
+ return sListenerMap.keyAt(index);
+ }
+ }
+ }
+
+ private static Object removeListener(int key) {
+ if (key == INVALID_KEY) return null;
+ synchronized (sListenerMapLock) {
+ Object listener = sListenerMap.get(key);
+ sListenerMap.remove(key);
+ return listener;
+ }
+ }
+
+ private static int removeListener(Object listener) {
+ int key = getListenerKey(listener);
+ if (key == INVALID_KEY) return key;
+ synchronized (sListenerMapLock) {
+ sListenerMap.remove(key);
+ return key;
+ }
+ }
+
+ private static class ServiceHandler extends Handler {
+ ServiceHandler(Looper looper) {
+ super(looper);
+ }
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+ } else {
+ Log.e(TAG, "Failed to set up channel connection");
+ // This will cause all further async API calls on the WifiManager
+ // to fail and throw an exception
+ sAsyncChannel = null;
+ }
+ sConnected.countDown();
+ return;
+ case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED:
+ return;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+ Log.e(TAG, "Channel connection lost");
+ // This will cause all further async API calls on the WifiManager
+ // to fail and throw an exception
+ sAsyncChannel = null;
+ getLooper().quit();
+ return;
+ }
+
+ Object listener = getListener(msg.arg2);
+ if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
+
+ switch (msg.what) {
+ /* ActionListeners grouped together */
+ case CMD_OP_SUCCEEDED :
+ ((ActionListener) listener).onSuccess(msg.obj);
+ break;
+ case CMD_OP_FAILED :
+ ((ActionListener) listener).onFailure(msg.arg1, msg.obj);
+ break;
+ case CMD_SCAN_RESULT :
+ ((ScanListener) listener).onResults(
+ ((ParcelableScanResults) msg.obj).getResults());
+ return;
+ case CMD_FULL_SCAN_RESULT :
+ FullScanResult result = (FullScanResult) msg.obj;
+ ((ScanListener) listener).onFullResult(result);
+ return;
+ case CMD_AP_FOUND:
+ ((HotlistListener) listener).onFound(
+ ((ParcelableScanResults) msg.obj).getResults());
+ return;
+ case CMD_WIFI_CHANGE_DETECTED:
+ ((SignificantWifiChangeListener) listener).onChanging(
+ ((ParcelableScanResults) msg.obj).getResults());
+ return;
+ case CMD_WIFI_CHANGES_STABILIZED:
+ ((SignificantWifiChangeListener) listener).onQuiescence(
+ ((ParcelableScanResults) msg.obj).getResults());
+ return;
+ default:
+ if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
+ return;
+ }
+ }
+ }
+}