Merge "Fix PanelView over expanding behavior." into lmp-preview-dev
diff --git a/api/current.txt b/api/current.txt
index f5c2707..79fb8df 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22862,16 +22862,6 @@
field public static final deprecated android.net.Uri DELETED_CONTENT_URI;
field public static final deprecated java.lang.String GROUP_ANDROID_STARRED = "Starred in Android";
field public static final deprecated java.lang.String GROUP_MY_CONTACTS = "Contacts";
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT = "non_syncable";
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT_TYPE = "android.local";
- field public static final java.lang.String _SYNC_ACCOUNT = "_sync_account";
- field public static final java.lang.String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
- field public static final java.lang.String _SYNC_DIRTY = "_sync_dirty";
- field public static final java.lang.String _SYNC_ID = "_sync_id";
- field public static final java.lang.String _SYNC_LOCAL_ID = "_sync_local_id";
- field public static final java.lang.String _SYNC_MARK = "_sync_mark";
- field public static final java.lang.String _SYNC_TIME = "_sync_time";
- field public static final java.lang.String _SYNC_VERSION = "_sync_version";
}
public static abstract deprecated interface Contacts.GroupsColumns {
@@ -22973,19 +22963,9 @@
field public static final deprecated android.net.Uri CONTENT_URI;
field public static final deprecated java.lang.String DEFAULT_SORT_ORDER = "name ASC";
field public static final deprecated android.net.Uri DELETED_CONTENT_URI;
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT = "non_syncable";
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT_TYPE = "android.local";
field public static final deprecated java.lang.String PRIMARY_EMAIL_ID = "primary_email";
field public static final deprecated java.lang.String PRIMARY_ORGANIZATION_ID = "primary_organization";
field public static final deprecated java.lang.String PRIMARY_PHONE_ID = "primary_phone";
- field public static final java.lang.String _SYNC_ACCOUNT = "_sync_account";
- field public static final java.lang.String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
- field public static final java.lang.String _SYNC_DIRTY = "_sync_dirty";
- field public static final java.lang.String _SYNC_ID = "_sync_id";
- field public static final java.lang.String _SYNC_LOCAL_ID = "_sync_local_id";
- field public static final java.lang.String _SYNC_MARK = "_sync_mark";
- field public static final java.lang.String _SYNC_TIME = "_sync_time";
- field public static final java.lang.String _SYNC_VERSION = "_sync_version";
}
public static final deprecated class Contacts.People.ContactMethods implements android.provider.BaseColumns android.provider.Contacts.ContactMethodsColumns android.provider.Contacts.PeopleColumns {
@@ -23048,16 +23028,6 @@
field public static final deprecated java.lang.String CONTENT_DIRECTORY = "photo";
field public static final deprecated android.net.Uri CONTENT_URI;
field public static final deprecated java.lang.String DEFAULT_SORT_ORDER = "person ASC";
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT = "non_syncable";
- field public static final java.lang.String NON_SYNCABLE_ACCOUNT_TYPE = "android.local";
- field public static final java.lang.String _SYNC_ACCOUNT = "_sync_account";
- field public static final java.lang.String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
- field public static final java.lang.String _SYNC_DIRTY = "_sync_dirty";
- field public static final java.lang.String _SYNC_ID = "_sync_id";
- field public static final java.lang.String _SYNC_LOCAL_ID = "_sync_local_id";
- field public static final java.lang.String _SYNC_MARK = "_sync_mark";
- field public static final java.lang.String _SYNC_TIME = "_sync_time";
- field public static final java.lang.String _SYNC_VERSION = "_sync_version";
}
public static abstract deprecated interface Contacts.PhotosColumns {
@@ -23378,7 +23348,6 @@
protected static abstract interface ContactsContract.ContactOptionsColumns {
field public static final java.lang.String CUSTOM_RINGTONE = "custom_ringtone";
field public static final java.lang.String LAST_TIME_CONTACTED = "last_time_contacted";
- field public static final java.lang.String PINNED = "pinned";
field public static final java.lang.String SEND_TO_VOICEMAIL = "send_to_voicemail";
field public static final java.lang.String STARRED = "starred";
field public static final java.lang.String TIMES_CONTACTED = "times_contacted";
@@ -23656,15 +23625,6 @@
field public static final int UNDEFINED = 0; // 0x0
}
- public static final class ContactsContract.PinnedPositions {
- ctor public ContactsContract.PinnedPositions();
- field public static final int DEMOTED = -1; // 0xffffffff
- field public static final java.lang.String STAR_WHEN_PINNING = "star_when_pinning";
- field public static final java.lang.String UNDEMOTE = "undemote";
- field public static final int UNPINNED = 2147483647; // 0x7fffffff
- field public static final android.net.Uri UPDATE_URI;
- }
-
public static final class ContactsContract.Preferences {
ctor public ContactsContract.Preferences();
field public static final java.lang.String DISPLAY_ORDER = "android.contacts.DISPLAY_ORDER";
diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java
index 9e2aacd..d4c5cfb 100644
--- a/core/java/android/provider/Contacts.java
+++ b/core/java/android/provider/Contacts.java
@@ -58,7 +58,7 @@
@Deprecated
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
- /**
+ /**
* Signifies an email address row that is stored in the ContactMethods table
* @deprecated see {@link android.provider.ContactsContract}
*/
@@ -337,7 +337,7 @@
* @deprecated see {@link android.provider.ContactsContract}
*/
@Deprecated
- public static final class People implements BaseColumns, SyncConstValue, PeopleColumns,
+ public static final class People implements BaseColumns, PeopleColumns,
PhonesColumns, PresenceColumns {
/**
* no public constructor since this is a utility class
@@ -790,7 +790,7 @@
*/
@Deprecated
public static final class Groups
- implements BaseColumns, SyncConstValue, GroupsColumns {
+ implements BaseColumns, GroupsColumns {
/**
* no public constructor since this is a utility class
*/
@@ -1864,7 +1864,7 @@
* @deprecated see {@link android.provider.ContactsContract}
*/
@Deprecated
- public static final class Photos implements BaseColumns, PhotosColumns, SyncConstValue {
+ public static final class Photos implements BaseColumns, PhotosColumns {
/**
* no public constructor since this is a utility class
*/
@@ -2199,7 +2199,7 @@
}
/** The action code to use when adding a contact
- * @deprecated see {@link android.provider.ContactsContract}
+ * @deprecated see {@link android.provider.ContactsContract}
*/
@Deprecated
public static final String ACTION = ContactsContract.Intents.Insert.ACTION;
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index b53ea81..8c7e879 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -184,9 +184,9 @@
public static final String DEFERRED_SNIPPETING_QUERY = "deferred_snippeting_query";
/**
- * A boolean parameter for {@link CommonDataKinds.Phone#CONTENT_URI},
- * {@link CommonDataKinds.Email#CONTENT_URI}, and
- * {@link CommonDataKinds.StructuredPostal#CONTENT_URI}.
+ * A boolean parameter for {@link CommonDataKinds.Phone#CONTENT_URI Phone.CONTENT_URI},
+ * {@link CommonDataKinds.Email#CONTENT_URI Email.CONTENT_URI}, and
+ * {@link CommonDataKinds.StructuredPostal#CONTENT_URI StructuredPostal.CONTENT_URI}.
* This enables a content provider to remove duplicate entries in results.
*/
public static final String REMOVE_DUPLICATE_ENTRIES = "remove_duplicate_entries";
@@ -244,6 +244,9 @@
public static final String KEY_AUTHORIZED_URI = "authorized_uri";
}
+ /*
+ * @hide
+ */
public static final class Preferences {
/**
@@ -808,6 +811,7 @@
* The position at which the contact is pinned. If {@link PinnedPositions#UNPINNED},
* the contact is not pinned. Also see {@link PinnedPositions}.
* <P>Type: INTEGER </P>
+ * @hide
*/
public static final String PINNED = "pinned";
@@ -7764,6 +7768,8 @@
* {@link PinnedPositions#STAR_WHEN_PINNING} to true to force all pinned and unpinned
* contacts to be automatically starred and unstarred.
* </p>
+ *
+ * @hide
*/
public static final class PinnedPositions {
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8af45db..865d92a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -364,6 +364,9 @@
<!-- If this is true, the screen will come on when you unplug usb/power/whatever. -->
<bool name="config_unplugTurnsOnScreen">false</bool>
+ <!-- Set this true only if the device has separate attention and notification lights. -->
+ <bool name="config_useAttentionLight">false</bool>
+
<!-- If this is true, the screen will fade off. -->
<bool name="config_animateScreenLights">true</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6a032e2..61b6a0d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1456,6 +1456,7 @@
<java-symbol type="array" name="config_defaultNotificationVibePattern" />
<java-symbol type="array" name="config_notificationFallbackVibePattern" />
<java-symbol type="array" name="config_onlySingleDcAllowed" />
+ <java-symbol type="bool" name="config_useAttentionLight" />
<java-symbol type="bool" name="config_animateScreenLights" />
<java-symbol type="bool" name="config_automatic_brightness_available" />
<java-symbol type="bool" name="config_enableFusedLocationOverlay" />
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
index 782ecd8..d5fea07 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -56,7 +56,6 @@
mHandler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
- Log.i(TAG, "handleMessage: "+msg.what);
ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
synchronized (this) {
if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 495db20..386402b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -163,6 +163,7 @@
private long[] mDefaultVibrationPattern;
private long[] mFallbackVibrationPattern;
+ private boolean mUseAttentionLight;
boolean mSystemReady;
private boolean mDisableNotificationAlerts;
@@ -182,7 +183,7 @@
new ArrayMap<String, NotificationRecord>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
- ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>();
+ ArrayList<String> mLights = new ArrayList<String>();
NotificationRecord mLedNotification;
private AppOpsManager mAppOps;
@@ -797,6 +798,8 @@
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
+ mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
+
// Don't start allowing notifications until the setup wizard has run once.
// After that, including subsequent boots, init with notifications turned on.
// This works on the first boot because the setup wizard will toggle this
@@ -1478,14 +1481,14 @@
}
}
- // 1. initial score: buckets of 10, around the app
- int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
+ // 1. initial score: buckets of 10, around the app [-20..20]
+ final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;
// 2. extract ranking signals from the notification data
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
user);
- NotificationRecord r = new NotificationRecord(n);
+ NotificationRecord r = new NotificationRecord(n, score);
NotificationRecord old = mNotificationsByKey.get(n.getKey());
if (old != null) {
// Retain ranking information from previous record
@@ -1507,13 +1510,13 @@
// blocked apps
if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
if (!isSystemNotification) {
- score = JUNK_SCORE;
+ r.score = JUNK_SCORE;
Slog.e(TAG, "Suppressing notification from package " + pkg
+ " by user request.");
}
}
- if (score < SCORE_DISPLAY_THRESHOLD) {
+ if (r.score < SCORE_DISPLAY_THRESHOLD) {
// Notification will be blocked because the score is too low.
return;
}
@@ -1529,28 +1532,16 @@
mUsageStats.registerUpdatedByApp(r, old);
// Make sure we don't lose the foreground service state.
notification.flags |=
- old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
+ old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
mNotificationsByKey.remove(old.sbn.getKey());
+ r.isUpdate = true;
}
mNotificationsByKey.put(n.getKey(), r);
applyZenModeLocked(r);
- // Should this notification make noise, vibe, or use the LED?
- final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) &&
- !r.isIntercepted();
- if (DBG || r.isIntercepted()) Slog.v(TAG,
- "pkg=" + pkg + " canInterrupt=" + canInterrupt +
- " intercept=" + r.isIntercepted());
Collections.sort(mNotificationList, mRankingComparator);
- // Ensure if this is a foreground service that the proper additional
- // flags are set.
- if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- notification.flags |= Notification.FLAG_ONGOING_EVENT
- | Notification.FLAG_NO_CLEAR;
- }
-
final int currentUser;
final long token = Binder.clearCallingIdentity();
try {
@@ -1571,20 +1562,11 @@
final long identity = Binder.clearCallingIdentity();
try {
mStatusBar.addNotification(n);
- if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0
- && canInterrupt) {
- mAttentionLight.pulse();
- }
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- // Send accessibility events only for the current user.
- if (currentUser == userId) {
- sendAccessibilityEvent(notification, pkg);
- }
-
- mListeners.notifyPostedLocked(r.sbn);
+ mListeners.notifyPostedLocked(n);
} else {
Slog.e(TAG, "Not posting notification with icon==0: " + notification);
if (old != null && !old.isCanceled) {
@@ -1595,7 +1577,7 @@
Binder.restoreCallingIdentity(identity);
}
- mListeners.notifyRemovedLocked(r.sbn);
+ mListeners.notifyRemovedLocked(n);
}
// ATTENTION: in a future release we will bail out here
// so that we do not play sounds, show lights, etc. for invalid
@@ -1604,136 +1586,14 @@
+ n.getPackageName());
}
- // If we're not supposed to beep, vibrate, etc. then don't.
- if (!mDisableNotificationAlerts
- && (!(old != null
- && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
- && (r.getUserId() == UserHandle.USER_ALL ||
- (r.getUserId() == userId && r.getUserId() == currentUser) ||
- mUserProfiles.isCurrentProfile(r.getUserId()))
- && canInterrupt
- && mSystemReady
- && mAudioManager != null) {
- if (DBG) Slog.v(TAG, "Interrupting!");
- // sound
-
- // should we use the default notification sound? (indicated either by
- // DEFAULT_SOUND or because notification.sound is pointing at
- // Settings.System.NOTIFICATION_SOUND)
- final boolean useDefaultSound =
- (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
- Settings.System.DEFAULT_NOTIFICATION_URI
- .equals(notification.sound);
-
- Uri soundUri = null;
- boolean hasValidSound = false;
-
- if (useDefaultSound) {
- soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
-
- // check to see if the default notification sound is silent
- ContentResolver resolver = getContext().getContentResolver();
- hasValidSound = Settings.System.getString(resolver,
- Settings.System.NOTIFICATION_SOUND) != null;
- } else if (notification.sound != null) {
- soundUri = notification.sound;
- hasValidSound = (soundUri != null);
- }
-
- if (hasValidSound) {
- boolean looping =
- (notification.flags & Notification.FLAG_INSISTENT) != 0;
- int audioStreamType;
- if (notification.audioStreamType >= 0) {
- audioStreamType = notification.audioStreamType;
- } else {
- audioStreamType = DEFAULT_STREAM_TYPE;
- }
- mSoundNotification = r;
- // do not play notifications if stream volume is 0 (typically because
- // ringer mode is silent) or if there is a user of exclusive audio focus
- if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
- && !mAudioManager.isAudioFocusExclusive()) {
- final long identity = Binder.clearCallingIdentity();
- try {
- final IRingtonePlayer player =
- mAudioManager.getRingtonePlayer();
- if (player != null) {
- if (DBG) Slog.v(TAG, "Playing sound " + soundUri
- + " on stream " + audioStreamType);
- player.playAsync(soundUri, user, looping, audioStreamType);
- }
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
- }
-
- // vibrate
- // Does the notification want to specify its own vibration?
- final boolean hasCustomVibrate = notification.vibrate != null;
-
- // new in 4.2: if there was supposed to be a sound and we're in vibrate
- // mode, and no other vibration is specified, we fall back to vibration
- final boolean convertSoundToVibration =
- !hasCustomVibrate
- && hasValidSound
- && (mAudioManager.getRingerMode()
- == AudioManager.RINGER_MODE_VIBRATE);
-
- // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
- final boolean useDefaultVibrate =
- (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
-
- if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
- && !(mAudioManager.getRingerMode()
- == AudioManager.RINGER_MODE_SILENT)) {
- mVibrateNotification = r;
-
- if (useDefaultVibrate || convertSoundToVibration) {
- // Escalate privileges so we can use the vibrator even if the
- // notifying app does not have the VIBRATE permission.
- long identity = Binder.clearCallingIdentity();
- try {
- mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
- useDefaultVibrate ? mDefaultVibrationPattern
- : mFallbackVibrationPattern,
- ((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1, notification.audioStreamType);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- } else if (notification.vibrate.length > 1) {
- // If you want your own vibration pattern, you need the VIBRATE
- // permission
- mVibrator.vibrate(r.sbn.getUid(), r.sbn.getOpPkg(),
- notification.vibrate,
- ((notification.flags & Notification.FLAG_INSISTENT) != 0)
- ? 0: -1, notification.audioStreamType);
- }
- }
+ // Ensure if this is a foreground service that the proper additional
+ // flags are set.
+ if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ notification.flags |= Notification.FLAG_ONGOING_EVENT
+ | Notification.FLAG_NO_CLEAR;
}
- // light
- // the most recent thing gets the light
- mLights.remove(old);
- if (mLedNotification == old) {
- mLedNotification = null;
- }
- //Slog.i(TAG, "notification.lights="
- // + ((old.notification.lights.flags & Notification.FLAG_SHOW_LIGHTS)
- // != 0));
- if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0
- && canInterrupt) {
- mLights.add(r);
- updateLightsLocked();
- } else {
- if (old != null
- && ((old.getFlags() & Notification.FLAG_SHOW_LIGHTS) != 0)) {
- updateLightsLocked();
- }
- }
+ buzzBeepBlinkLocked(r);
}
}
});
@@ -1741,6 +1601,158 @@
idOut[0] = id;
}
+ private void buzzBeepBlinkLocked(NotificationRecord record) {
+ final Notification notification = record.sbn.getNotification();
+
+ // Should this notification make noise, vibe, or use the LED?
+ final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) &&
+ !record.isIntercepted();
+ if (DBG || record.isIntercepted())
+ Slog.v(TAG,
+ "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
+ " intercept=" + record.isIntercepted()
+ );
+
+ final int currentUser;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ currentUser = ActivityManager.getCurrentUser();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ // If we're not supposed to beep, vibrate, etc. then don't.
+ if (!mDisableNotificationAlerts
+ && (!(record.isUpdate
+ && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
+ && (record.getUserId() == UserHandle.USER_ALL ||
+ record.getUserId() == currentUser ||
+ mUserProfiles.isCurrentProfile(record.getUserId()))
+ && canInterrupt
+ && mSystemReady
+ && mAudioManager != null) {
+ if (DBG) Slog.v(TAG, "Interrupting!");
+
+ sendAccessibilityEvent(notification, record.sbn.getPackageName());
+
+ // sound
+
+ // should we use the default notification sound? (indicated either by
+ // DEFAULT_SOUND or because notification.sound is pointing at
+ // Settings.System.NOTIFICATION_SOUND)
+ final boolean useDefaultSound =
+ (notification.defaults & Notification.DEFAULT_SOUND) != 0 ||
+ Settings.System.DEFAULT_NOTIFICATION_URI
+ .equals(notification.sound);
+
+ Uri soundUri = null;
+ boolean hasValidSound = false;
+
+ if (useDefaultSound) {
+ soundUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+
+ // check to see if the default notification sound is silent
+ ContentResolver resolver = getContext().getContentResolver();
+ hasValidSound = Settings.System.getString(resolver,
+ Settings.System.NOTIFICATION_SOUND) != null;
+ } else if (notification.sound != null) {
+ soundUri = notification.sound;
+ hasValidSound = (soundUri != null);
+ }
+
+ if (hasValidSound) {
+ boolean looping =
+ (notification.flags & Notification.FLAG_INSISTENT) != 0;
+ int audioStreamType;
+ if (notification.audioStreamType >= 0) {
+ audioStreamType = notification.audioStreamType;
+ } else {
+ audioStreamType = DEFAULT_STREAM_TYPE;
+ }
+ mSoundNotification = record;
+ // do not play notifications if stream volume is 0 (typically because
+ // ringer mode is silent) or if there is a user of exclusive audio focus
+ if ((mAudioManager.getStreamVolume(audioStreamType) != 0)
+ && !mAudioManager.isAudioFocusExclusive()) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ final IRingtonePlayer player =
+ mAudioManager.getRingtonePlayer();
+ if (player != null) {
+ if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+ + " on stream " + audioStreamType);
+ player.playAsync(soundUri, record.sbn.getUser(), looping,
+ audioStreamType);
+ }
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ // vibrate
+ // Does the notification want to specify its own vibration?
+ final boolean hasCustomVibrate = notification.vibrate != null;
+
+ // new in 4.2: if there was supposed to be a sound and we're in vibrate
+ // mode, and no other vibration is specified, we fall back to vibration
+ final boolean convertSoundToVibration =
+ !hasCustomVibrate
+ && hasValidSound
+ && (mAudioManager.getRingerMode()
+ == AudioManager.RINGER_MODE_VIBRATE);
+
+ // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
+ final boolean useDefaultVibrate =
+ (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
+
+ if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate)
+ && !(mAudioManager.getRingerMode()
+ == AudioManager.RINGER_MODE_SILENT)) {
+ mVibrateNotification = record;
+
+ if (useDefaultVibrate || convertSoundToVibration) {
+ // Escalate privileges so we can use the vibrator even if the
+ // notifying app does not have the VIBRATE permission.
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ useDefaultVibrate ? mDefaultVibrationPattern
+ : mFallbackVibrationPattern,
+ ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+ ? 0: -1, notification.audioStreamType);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } else if (notification.vibrate.length > 1) {
+ // If you want your own vibration pattern, you need the VIBRATE
+ // permission
+ mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ notification.vibrate,
+ ((notification.flags & Notification.FLAG_INSISTENT) != 0)
+ ? 0: -1, notification.audioStreamType);
+ }
+ }
+ }
+
+ // light
+ // release the light
+ boolean wasShowLights = mLights.remove(record.getKey());
+ if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) {
+ mLedNotification = null;
+ }
+ if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) {
+ mLights.add(record.getKey());
+ updateLightsLocked();
+ if (mUseAttentionLight) {
+ mAttentionLight.pulse();
+ }
+ } else if (wasShowLights) {
+ updateLightsLocked();
+ }
+ }
+
void showNextToastLocked() {
ToastRecord record = mToastQueue.get(0);
while (record != null) {
@@ -1866,6 +1878,9 @@
int indexAfter = findNotificationRecordIndexLocked(record);
boolean interceptAfter = record.isIntercepted();
changed = indexBefore != indexAfter || interceptBefore != interceptAfter;
+ if (interceptBefore && !interceptAfter) {
+ buzzBeepBlinkLocked(record);
+ }
}
if (changed) {
scheduleSendRankingUpdate();
@@ -2011,7 +2026,7 @@
}
// light
- mLights.remove(r);
+ mLights.remove(r.getKey());
if (mLedNotification == r) {
mLedNotification = null;
}
@@ -2196,7 +2211,7 @@
// get next notification, if any
int n = mLights.size();
if (n > 0) {
- mLedNotification = mLights.get(n-1);
+ mLedNotification = mNotificationsByKey.get(mLights.get(n-1));
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 08f8eb4..30d4fec 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -42,6 +42,7 @@
final StatusBarNotification sbn;
NotificationUsageStats.SingleNotificationStats stats;
boolean isCanceled;
+ int score;
// These members are used by NotificationSignalExtractors
// to communicate with the ranking module.
@@ -53,9 +54,13 @@
// InterceptedNotifications needs to know if this has been previously evaluated.
private boolean mTouchedByZen;
- NotificationRecord(StatusBarNotification sbn)
+ // Is this record an update of an old record?
+ public boolean isUpdate;
+
+ NotificationRecord(StatusBarNotification sbn, int score)
{
this.sbn = sbn;
+ this.score = score;
}
// copy any notes that the ranking system may have made before the update