Merge "support the people extra on the notification object"
This commit is contained in:
@@ -4453,6 +4453,7 @@ package android.app {
|
||||
ctor public Notification.Builder(android.content.Context);
|
||||
method public android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
|
||||
method public android.app.Notification.Builder addExtras(android.os.Bundle);
|
||||
method public android.app.Notification.Builder addPerson(java.lang.String);
|
||||
method public android.app.Notification build();
|
||||
method public android.os.Bundle getExtras();
|
||||
method public deprecated android.app.Notification getNotification();
|
||||
|
||||
@@ -1311,6 +1311,7 @@ public class Notification implements Parcelable
|
||||
private Notification mPublicVersion = null;
|
||||
private boolean mQuantumTheme;
|
||||
private final LegacyNotificationUtil mLegacyNotificationUtil;
|
||||
private ArrayList<String> mPeople;
|
||||
|
||||
/**
|
||||
* Constructs a new Builder with the defaults:
|
||||
@@ -1338,6 +1339,7 @@ public class Notification implements Parcelable
|
||||
mWhen = System.currentTimeMillis();
|
||||
mAudioStreamType = STREAM_DEFAULT;
|
||||
mPriority = PRIORITY_DEFAULT;
|
||||
mPeople = new ArrayList<String>();
|
||||
|
||||
// TODO: Decide on targetSdk from calling app whether to use quantum theme.
|
||||
mQuantumTheme = true;
|
||||
@@ -1722,6 +1724,16 @@ public class Notification implements Parcelable
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a person that is relevant to this notification.
|
||||
*
|
||||
* @see Notification#EXTRA_PEOPLE
|
||||
*/
|
||||
public Builder addPerson(String handle) {
|
||||
mPeople.add(handle);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge additional metadata into this notification.
|
||||
*
|
||||
@@ -2149,6 +2161,9 @@ public class Notification implements Parcelable
|
||||
if (mLargeIcon != null) {
|
||||
extras.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
|
||||
}
|
||||
if (!mPeople.isEmpty()) {
|
||||
extras.putStringArray(EXTRA_PEOPLE, mPeople.toArray(new String[mPeople.size()]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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.internal.notification;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.ContactsContract.Contacts;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.LruCache;
|
||||
import android.util.Slog;
|
||||
|
||||
/**
|
||||
* This {@link NotificationScorer} attempts to validate people references.
|
||||
* Also elevates the priority of real people.
|
||||
*/
|
||||
public class PeopleNotificationScorer implements NotificationScorer {
|
||||
private static final String TAG = "PeopleNotificationScorer";
|
||||
private static final boolean DBG = false;
|
||||
|
||||
private static final boolean ENABLE_PEOPLE_SCORER = true;
|
||||
private static final String SETTING_ENABLE_PEOPLE_SCORER = "people_scorer_enabled";
|
||||
private static final String[] LOOKUP_PROJECTION = { Contacts._ID };
|
||||
private static final int MAX_PEOPLE = 10;
|
||||
private static final int PEOPLE_CACHE_SIZE = 200;
|
||||
// see NotificationManagerService
|
||||
private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
|
||||
|
||||
protected boolean mEnabled;
|
||||
private Context mContext;
|
||||
|
||||
// maps raw person handle to resolved person object
|
||||
private LruCache<String, LookupResult> mPeopleCache;
|
||||
|
||||
private float findMaxContactScore(Bundle extras) {
|
||||
if (extras == null) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
final String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE);
|
||||
if (people == null || people.length == 0) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
float rank = 0f;
|
||||
for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
|
||||
final String handle = people[personIdx];
|
||||
if (TextUtils.isEmpty(handle)) continue;
|
||||
|
||||
LookupResult lookupResult = mPeopleCache.get(handle);
|
||||
if (lookupResult == null || lookupResult.isExpired()) {
|
||||
final Uri uri = Uri.parse(handle);
|
||||
if ("tel".equals(uri.getScheme())) {
|
||||
if (DBG) Slog.w(TAG, "checking telephone URI: " + handle);
|
||||
lookupResult = lookupPhoneContact(handle, uri.getSchemeSpecificPart());
|
||||
} else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
|
||||
if (DBG) Slog.w(TAG, "checking lookup URI: " + handle);
|
||||
lookupResult = resolveContactsUri(handle, uri);
|
||||
} else {
|
||||
if (DBG) Slog.w(TAG, "unsupported URI " + handle);
|
||||
}
|
||||
} else {
|
||||
if (DBG) Slog.w(TAG, "using cached lookupResult: " + lookupResult.mId);
|
||||
}
|
||||
if (lookupResult != null) {
|
||||
rank = Math.max(rank, lookupResult.getRank());
|
||||
}
|
||||
}
|
||||
return rank;
|
||||
}
|
||||
|
||||
private LookupResult lookupPhoneContact(final String handle, final String number) {
|
||||
LookupResult lookupResult = null;
|
||||
Cursor c = null;
|
||||
try {
|
||||
Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
|
||||
Uri.encode(number));
|
||||
c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null);
|
||||
if (c != null && c.getCount() > 0) {
|
||||
c.moveToFirst();
|
||||
final int idIdx = c.getColumnIndex(Contacts._ID);
|
||||
final int id = c.getInt(idIdx);
|
||||
if (DBG) Slog.w(TAG, "is valid: " + id);
|
||||
lookupResult = new LookupResult(id);
|
||||
}
|
||||
} catch(Throwable t) {
|
||||
Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
|
||||
} finally {
|
||||
if (c != null) {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
if (lookupResult == null) {
|
||||
lookupResult = new LookupResult(LookupResult.INVALID_ID);
|
||||
}
|
||||
mPeopleCache.put(handle, lookupResult);
|
||||
return lookupResult;
|
||||
}
|
||||
|
||||
private LookupResult resolveContactsUri(String handle, final Uri personUri) {
|
||||
LookupResult lookupResult = null;
|
||||
Cursor c = null;
|
||||
try {
|
||||
c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null);
|
||||
if (c != null && c.getCount() > 0) {
|
||||
c.moveToFirst();
|
||||
final int idIdx = c.getColumnIndex(Contacts._ID);
|
||||
final int id = c.getInt(idIdx);
|
||||
if (DBG) Slog.w(TAG, "is valid: " + id);
|
||||
lookupResult = new LookupResult(id);
|
||||
}
|
||||
} catch(Throwable t) {
|
||||
Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
|
||||
} finally {
|
||||
if (c != null) {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
if (lookupResult == null) {
|
||||
lookupResult = new LookupResult(LookupResult.INVALID_ID);
|
||||
}
|
||||
mPeopleCache.put(handle, lookupResult);
|
||||
return lookupResult;
|
||||
}
|
||||
|
||||
private final static int clamp(int x, int low, int high) {
|
||||
return (x < low) ? low : ((x > high) ? high : x);
|
||||
}
|
||||
|
||||
// TODO: rework this function before shipping
|
||||
private static int priorityBumpMap(int incomingScore) {
|
||||
//assumption is that scale runs from [-2*pm, 2*pm]
|
||||
int pm = NOTIFICATION_PRIORITY_MULTIPLIER;
|
||||
int theScore = incomingScore;
|
||||
// enforce input in range
|
||||
theScore = clamp(theScore, -2 * pm, 2 * pm);
|
||||
if (theScore != incomingScore) return incomingScore;
|
||||
// map -20 -> -20 and -10 -> 5 (when pm = 10)
|
||||
if (theScore <= -pm) {
|
||||
theScore += 1.5 * (theScore + 2 * pm);
|
||||
} else {
|
||||
// map 0 -> 10, 10 -> 15, 20 -> 20;
|
||||
theScore += 0.5 * (2 * pm - theScore);
|
||||
}
|
||||
if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore
|
||||
+ ", score after " + theScore + ".");
|
||||
return theScore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(Context context) {
|
||||
if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + ".");
|
||||
mContext = context;
|
||||
mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
|
||||
mEnabled = ENABLE_PEOPLE_SCORER && 1 == Settings.Global.getInt(
|
||||
mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_SCORER, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScore(Notification notification, int score) {
|
||||
if (notification == null || !mEnabled) {
|
||||
if (DBG) Slog.w(TAG, "empty notification? scorer disabled?");
|
||||
return score;
|
||||
}
|
||||
float contactScore = findMaxContactScore(notification.extras);
|
||||
if (contactScore > 0f) {
|
||||
if (DBG) Slog.v(TAG, "Notification references a real contact. Promoted!");
|
||||
score = priorityBumpMap(score);
|
||||
} else {
|
||||
if (DBG) Slog.v(TAG, "Notification lacks any valid contact reference. Not promoted!");
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
private static class LookupResult {
|
||||
private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
|
||||
public static final int INVALID_ID = -1;
|
||||
|
||||
private final long mExpireMillis;
|
||||
private int mId;
|
||||
|
||||
public LookupResult(int id) {
|
||||
mId = id;
|
||||
mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return mExpireMillis < System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public boolean isInvalid() {
|
||||
return mId == INVALID_ID || isExpired();
|
||||
}
|
||||
|
||||
public float getRank() {
|
||||
if (isInvalid()) {
|
||||
return 0f;
|
||||
} else {
|
||||
return 1f; // TODO: finer grained score
|
||||
}
|
||||
}
|
||||
|
||||
public LookupResult setId(int id) {
|
||||
mId = id;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1370,7 +1370,7 @@
|
||||
</string-array>
|
||||
|
||||
<string-array name="config_notificationScorers">
|
||||
<item>com.android.internal.notification.DemoContactNotificationScorer</item>
|
||||
<item>com.android.internal.notification.PeopleNotificationScorer</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Flag indicating that this device does not rotate and will always remain in its default
|
||||
|
||||
Reference in New Issue
Block a user