Add birthdays to People Space tiles

Birthdays only appear if no notification content is currently present.
They are removed as a status once no longer returned from the Contacts
DB, meaning if removed from the Contacts DB or if queried the day after
the birthday.

Test: in-progress
Change-Id: I35a6a5e5644de281220b7af7c2779e06f0233c47
This commit is contained in:
Anna Zappone
2020-12-14 13:33:07 +00:00
parent c103f31eef
commit 9daebe0748
8 changed files with 497 additions and 158 deletions

View File

@@ -17,6 +17,7 @@
package android.app.people;
import android.annotation.NonNull;
import android.app.Person;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
@@ -44,6 +45,7 @@ public class PeopleSpaceTile implements Parcelable {
private int mUid;
private Uri mContactUri;
private String mPackageName;
private String mStatusText;
private long mLastInteractionTimestamp;
private boolean mIsImportantConversation;
private boolean mIsHiddenConversation;
@@ -61,6 +63,7 @@ public class PeopleSpaceTile implements Parcelable {
mContactUri = b.mContactUri;
mUid = b.mUid;
mPackageName = b.mPackageName;
mStatusText = b.mStatusText;
mLastInteractionTimestamp = b.mLastInteractionTimestamp;
mIsImportantConversation = b.mIsImportantConversation;
mIsHiddenConversation = b.mIsHiddenConversation;
@@ -95,6 +98,10 @@ public class PeopleSpaceTile implements Parcelable {
return mPackageName;
}
public String getStatusText() {
return mStatusText;
}
/** Returns the timestamp of the last interaction. */
public long getLastInteractionTimestamp() {
return mLastInteractionTimestamp;
@@ -148,6 +155,7 @@ public class PeopleSpaceTile implements Parcelable {
builder.setContactUri(mContactUri);
builder.setUid(mUid);
builder.setPackageName(mPackageName);
builder.setStatusText(mStatusText);
builder.setLastInteractionTimestamp(mLastInteractionTimestamp);
builder.setIsImportantConversation(mIsImportantConversation);
builder.setIsHiddenConversation(mIsHiddenConversation);
@@ -165,6 +173,7 @@ public class PeopleSpaceTile implements Parcelable {
private Uri mContactUri;
private int mUid;
private String mPackageName;
private String mStatusText;
private long mLastInteractionTimestamp;
private boolean mIsImportantConversation;
private boolean mIsHiddenConversation;
@@ -188,6 +197,16 @@ public class PeopleSpaceTile implements Parcelable {
mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0));
mUid = info.getUserId();
mPackageName = info.getPackage();
mContactUri = getContactUri(info);
}
private Uri getContactUri(ShortcutInfo info) {
if (info.getPersons() == null || info.getPersons().length != 1) {
return null;
}
// TODO(b/175584929): Update to use the Uri from PeopleService directly
Person person = info.getPersons()[0];
return person.getUri() == null ? null : Uri.parse(person.getUri());
}
/** Sets the ID for the tile. */
@@ -226,6 +245,12 @@ public class PeopleSpaceTile implements Parcelable {
return this;
}
/** Sets the status text. */
public Builder setStatusText(String statusText) {
mStatusText = statusText;
return this;
}
/** Sets the last interaction timestamp. */
public Builder setLastInteractionTimestamp(long lastInteractionTimestamp) {
mLastInteractionTimestamp = lastInteractionTimestamp;
@@ -279,8 +304,10 @@ public class PeopleSpaceTile implements Parcelable {
mId = in.readString();
mUserName = in.readCharSequence();
mUserIcon = in.readParcelable(Icon.class.getClassLoader());
mContactUri = in.readParcelable(Uri.class.getClassLoader());
mUid = in.readInt();
mPackageName = in.readString();
mStatusText = in.readString();
mLastInteractionTimestamp = in.readLong();
mIsImportantConversation = in.readBoolean();
mIsHiddenConversation = in.readBoolean();
@@ -300,8 +327,10 @@ public class PeopleSpaceTile implements Parcelable {
dest.writeString(mId);
dest.writeCharSequence(mUserName);
dest.writeParcelable(mUserIcon, flags);
dest.writeParcelable(mContactUri, flags);
dest.writeInt(mUid);
dest.writeString(mPackageName);
dest.writeString(mStatusText);
dest.writeLong(mLastInteractionTimestamp);
dest.writeBoolean(mIsImportantConversation);
dest.writeBoolean(mIsHiddenConversation);

View File

@@ -169,14 +169,16 @@
</LinearLayout>
<LinearLayout
android:background="@drawable/people_space_content_background"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/content"
android:paddingVertical="3dp"
android:paddingHorizontal="12dp"
android:gravity="center"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textSize="14sp"
android:textSize="16sp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:maxLines="2"

View File

@@ -2773,6 +2773,14 @@
<string name="basic_status" translatable="false">Open conversation</string>
<!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
<string name="select_conversation_text" translatable="false">Select one conversation to show in your widget:</string>
<!-- Timestamp for notification with exact time [CHAR LIMIT=120] -->
<string name="timestamp" translatable="false"><xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
<!-- Timestamp for notification when less than a certain time window [CHAR LIMIT=120] -->
<string name="less_than_timestamp" translatable="false">Less than <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
<!-- Timestamp for notification when over a certain time window [CHAR LIMIT=120] -->
<string name="over_timestamp" translatable="false">Over <xliff:g id="duration" example="1 week">%1$s</xliff:g> ago</string>
<!-- Status text for a birthday today [CHAR LIMIT=120] -->
<string name="birthday_status" translatable="false">Today is their birthday!</string>
<!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
[CHAR LIMIT=NONE] -->

View File

@@ -44,7 +44,6 @@ import com.android.systemui.R;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import java.util.List;
import java.util.Map;
/**
* Shows the user their tiles for their priority People (go/live-status).
@@ -96,13 +95,12 @@ public class PeopleSpaceActivity extends Activity {
*/
private void setTileViewsWithPriorityConversations() {
try {
List<Map.Entry<Long, PeopleSpaceTile>> tiles = PeopleSpaceUtils.getTiles(
List<PeopleSpaceTile> tiles = PeopleSpaceUtils.getTiles(
mContext, mNotificationManager, mPeopleManager, mLauncherApps);
for (Map.Entry<Long, PeopleSpaceTile> entry : tiles) {
PeopleSpaceTile tile = entry.getValue();
for (PeopleSpaceTile tile : tiles) {
PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext, mPeopleSpaceLayout,
tile.getId());
setTileView(tileView, tile, entry.getKey());
setTileView(tileView, tile);
}
} catch (Exception e) {
Log.e(TAG, "Couldn't retrieve conversations", e);
@@ -110,12 +108,12 @@ public class PeopleSpaceActivity extends Activity {
}
/** Sets {@code tileView} with the data in {@code conversation}. */
private void setTileView(PeopleSpaceTileView tileView, PeopleSpaceTile tile,
long lastInteraction) {
private void setTileView(PeopleSpaceTileView tileView, PeopleSpaceTile tile) {
try {
String pkg = tile.getPackageName();
String status =
PeopleSpaceUtils.getLastInteractionString(mContext, lastInteraction);
PeopleSpaceUtils.getLastInteractionString(mContext,
tile.getLastInteractionTimestamp(), true);
tileView.setStatus(status);
tileView.setName(tile.getUserName().toString());

View File

@@ -29,6 +29,8 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.database.Cursor;
import android.database.SQLException;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
@@ -41,7 +43,7 @@ import android.os.Bundle;
import android.os.Parcelable;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
@@ -49,17 +51,24 @@ import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
import androidx.preference.PreferenceManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ArrayUtils;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -114,7 +123,7 @@ public class PeopleSpaceUtils {
}
/** Returns a list of map entries corresponding to user's conversations. */
public static List<Map.Entry<Long, PeopleSpaceTile>> getTiles(
public static List<PeopleSpaceTile> getTiles(
Context context, INotificationManager notificationManager, IPeopleManager peopleManager,
LauncherApps launcherApps)
throws Exception {
@@ -122,70 +131,72 @@ public class PeopleSpaceUtils {
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 1;
List<ConversationChannelWrapper> conversations = notificationManager.getConversations(
true).getList();
List<Map.Entry<Long, PeopleSpaceTile>> tiles = getSortedTiles(peopleManager,
conversations.stream().map(c ->
new PeopleSpaceTile.Builder(c.getShortcutInfo(), launcherApps).build()));
List<PeopleSpaceTile> tiles = getSortedTiles(peopleManager,
conversations.stream().filter(c -> c.getShortcutInfo() != null).map(
c -> new PeopleSpaceTile.Builder(c.getShortcutInfo(),
launcherApps).build()));
if (!showOnlyPriority) {
if (DEBUG) Log.d(TAG, "Add recent conversations");
List<ConversationChannel> recentConversations =
peopleManager.getRecentConversations().getList();
List<Map.Entry<Long, PeopleSpaceTile>> recentTiles =
getSortedTiles(peopleManager, recentConversations.stream().map(c ->
new PeopleSpaceTile
.Builder(c.getShortcutInfo(), launcherApps)
.build()));
List<PeopleSpaceTile> recentTiles =
getSortedTiles(peopleManager,
recentConversations
.stream()
.filter(
c -> c.getShortcutInfo() != null)
.map(
c -> new PeopleSpaceTile.Builder(c.getShortcutInfo(),
launcherApps).build()));
tiles.addAll(recentTiles);
}
return tiles;
}
/** Updates {@code appWidgetIds} with their associated conversation stored. */
/**
* Updates {@code appWidgetIds} with their associated conversation stored, handling a
* notification being posted or removed.
*/
public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds,
AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
IPeopleManager peopleManager = IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE));
LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>();
try {
List<Map.Entry<Long, PeopleSpaceTile>> tiles =
List<PeopleSpaceTile> tiles =
PeopleSpaceUtils.getTiles(context, notificationManager,
peopleManager, launcherApps);
for (int appWidgetId : appWidgetIds) {
String shortcutId = sp.getString(String.valueOf(appWidgetId), null);
if (DEBUG) {
Log.d(TAG, "Set widget: " + appWidgetId + " with shortcut ID: " + shortcutId);
Log.d(TAG, "Widget ID: " + appWidgetId + " Shortcut ID: " + shortcutId);
}
Optional<Map.Entry<Long, PeopleSpaceTile>> entry = tiles.stream().filter(
e -> e.getValue().getId().equals(shortcutId)).findFirst();
Optional<PeopleSpaceTile> entry = tiles.stream().filter(
e -> e.getId().equals(shortcutId)).findFirst();
if (!entry.isPresent() || shortcutId == null) {
if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
//TODO: Delete app widget id when crash is fixed (b/175486868)
continue;
}
PeopleSpaceTile tile =
augmentTileFromStorage(entry.get().getValue(), appWidgetManager,
appWidgetId);
RemoteViews views = createRemoteViews(context, tile, entry.get().getKey(),
// Augment current tile based on stored fields.
PeopleSpaceTile tile = augmentTileFromStorage(entry.get(), appWidgetManager,
appWidgetId);
RemoteViews views = createRemoteViews(context, tile, appWidgetId);
// Tell the AppWidgetManager to perform an update on the current app widget.
appWidgetManager.updateAppWidget(appWidgetId, views);
widgetIdToTile.put(appWidgetId, tile);
}
} catch (Exception e) {
Log.e(TAG, "Exception updating single conversation widgets: " + e);
Log.e(TAG, "Failed to retrieve conversations to set tiles: " + e);
}
}
/** Returns a list sorted by ascending last interaction time from {@code stream}. */
private static List<Map.Entry<Long, PeopleSpaceTile>> getSortedTiles(
IPeopleManager peopleManager, Stream<PeopleSpaceTile> stream) {
return stream
.filter(c -> shouldKeepConversation(c))
.map(c -> Map.entry(getLastInteraction(peopleManager, c), c))
.sorted((c1, c2) -> (c2.getKey().compareTo(c1.getKey())))
.collect(Collectors.toList());
getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
}
/** Augment {@link PeopleSpaceTile} with fields from stored tile. */
@@ -198,6 +209,7 @@ public class PeopleSpaceUtils {
return tile;
}
return tile.toBuilder()
.setStatusText(storedTile.getStatusText())
.setNotificationKey(storedTile.getNotificationKey())
.setNotificationContent(storedTile.getNotificationContent())
.setNotificationDataUri(storedTile.getNotificationDataUri())
@@ -234,37 +246,67 @@ public class PeopleSpaceUtils {
.setNotificationDataUri(null)
.build();
}
updateAppWidgetOptions(appWidgetManager, appWidgetId, storedTile);
}
private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
PeopleSpaceTile tile) {
Bundle newOptions = new Bundle();
newOptions.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, storedTile);
newOptions.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, tile);
appWidgetManager.updateAppWidgetOptions(appWidgetId, newOptions);
}
private static RemoteViews createRemoteViews(Context context, PeopleSpaceTile tile,
long lastInteraction, int appWidgetId) throws Exception {
// TODO: If key is null or if text and data uri are null.
if (tile.getNotificationKey() == null) {
return createLastInteractionRemoteViews(context, tile, lastInteraction, appWidgetId);
/** Creates a {@link RemoteViews} for {@code tile}. */
private static RemoteViews createRemoteViews(Context context,
PeopleSpaceTile tile, int appWidgetId) {
RemoteViews views;
if (tile.getNotificationKey() != null) {
views = createNotificationRemoteViews(context, tile);
} else if (tile.getStatusText() != null) {
views = createStatusRemoteViews(context, tile);
} else {
views = createLastInteractionRemoteViews(context, tile);
}
return createNotificationRemoteViews(context, tile, lastInteraction, appWidgetId);
return setCommonRemoteViewsFields(context, views, tile, appWidgetId);
}
private static RemoteViews createLastInteractionRemoteViews(Context context,
PeopleSpaceTile tile, long lastInteraction, int appWidgetId)
throws Exception {
RemoteViews views = new RemoteViews(
context.getPackageName(), R.layout.people_space_large_avatar_tile);
String status = PeopleSpaceUtils.getLastInteractionString(
context, lastInteraction);
views.setTextViewText(R.id.status, status);
private static RemoteViews setCommonRemoteViewsFields(Context context, RemoteViews views,
PeopleSpaceTile tile, int appWidgetId) {
try {
views.setTextViewText(R.id.name, tile.getUserName().toString());
views.setImageViewBitmap(
R.id.package_icon,
PeopleSpaceUtils.convertDrawableToBitmap(
context.getPackageManager().getApplicationIcon(
tile.getPackageName())
)
);
views.setImageViewIcon(R.id.person_icon, tile.getUserIcon());
views = setCommonRemoteViewsFields(context, views, tile, appWidgetId);
return views;
Intent activityIntent = new Intent(context, LaunchConversationActivity.class);
activityIntent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NO_HISTORY
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId());
activityIntent.putExtra(
PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName());
activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid());
views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity(
context,
appWidgetId,
activityIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE));
return views;
} catch (Exception e) {
Log.e(TAG, "Failed to set common fields: " + e);
}
return null;
}
private static RemoteViews createNotificationRemoteViews(Context context,
PeopleSpaceTile tile, long lastInteraction, int appWidgetId)
throws Exception {
PeopleSpaceTile tile) {
RemoteViews views = new RemoteViews(
context.getPackageName(), R.layout.people_space_small_avatar_tile);
Uri image = tile.getNotificationDataUri();
@@ -280,38 +322,26 @@ public class PeopleSpaceUtils {
views.setViewVisibility(R.id.content, View.VISIBLE);
views.setViewVisibility(R.id.image, View.GONE);
}
views = setCommonRemoteViewsFields(context, views, tile, appWidgetId);
views.setTextViewText(R.id.time, PeopleSpaceUtils.getLastInteractionString(
context, tile.getLastInteractionTimestamp(), false));
return views;
}
private static RemoteViews setCommonRemoteViewsFields(
Context context, RemoteViews views, PeopleSpaceTile tile, int appWidgetId)
throws Exception {
views.setTextViewText(R.id.name, tile.getUserName().toString());
views.setImageViewBitmap(
R.id.package_icon,
PeopleSpaceUtils.convertDrawableToBitmap(
context.getPackageManager().getApplicationIcon(tile.getPackageName())
)
);
views.setImageViewIcon(R.id.person_icon, tile.getUserIcon());
private static RemoteViews createStatusRemoteViews(Context context,
PeopleSpaceTile tile) {
RemoteViews views = new RemoteViews(
context.getPackageName(), R.layout.people_space_large_avatar_tile);
views.setTextViewText(R.id.status, tile.getStatusText());
return views;
}
Intent activityIntent = new Intent(context, LaunchConversationActivity.class);
activityIntent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NO_HISTORY
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId());
activityIntent.putExtra(
PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName());
activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid());
views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity(
context,
appWidgetId,
activityIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE));
private static RemoteViews createLastInteractionRemoteViews(Context context,
PeopleSpaceTile tile) {
RemoteViews views = new RemoteViews(
context.getPackageName(), R.layout.people_space_large_avatar_tile);
String status = PeopleSpaceUtils.getLastInteractionString(
context, tile.getLastInteractionTimestamp(), true);
views.setTextViewText(R.id.status, status);
return views;
}
@@ -319,7 +349,7 @@ public class PeopleSpaceUtils {
RemoteViews views, CharSequence content) {
String punctuation = getBackgroundTextFromMessage(content.toString());
int visibility = View.GONE;
if (punctuation != null) {
if (punctuation != null) {
visibility = View.VISIBLE;
}
views.setTextViewText(R.id.punctuation1, punctuation);
@@ -364,6 +394,7 @@ public class PeopleSpaceUtils {
}
/** Gets the most recent {@link Notification.MessagingStyle.Message} from the notification. */
@VisibleForTesting
public static Notification.MessagingStyle.Message getLastMessagingStyleMessage(
StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
@@ -384,6 +415,18 @@ public class PeopleSpaceUtils {
return null;
}
/** Returns a list sorted by ascending last interaction time from {@code stream}. */
private static List<PeopleSpaceTile> getSortedTiles(IPeopleManager peopleManager,
Stream<PeopleSpaceTile> stream) {
return stream
.filter(c -> shouldKeepConversation(c))
.map(c -> c.toBuilder().setLastInteractionTimestamp(
getLastInteraction(peopleManager, c)).build())
.sorted((c1, c2) -> new Long(c2.getLastInteractionTimestamp()).compareTo(
new Long(c1.getLastInteractionTimestamp())))
.collect(Collectors.toList());
}
/** Returns the last interaction time with the user specified by {@code PeopleSpaceTile}. */
private static Long getLastInteraction(IPeopleManager peopleManager,
PeopleSpaceTile tile) {
@@ -426,7 +469,8 @@ public class PeopleSpaceUtils {
}
/** Returns a readable status describing the {@code lastInteraction}. */
public static String getLastInteractionString(Context context, long lastInteraction) {
public static String getLastInteractionString(Context context, long lastInteraction,
boolean includeLastChatted) {
if (lastInteraction == 0L) {
Log.e(TAG, "Could not get valid last interaction");
return context.getString(R.string.basic_status);
@@ -436,18 +480,25 @@ public class PeopleSpaceUtils {
MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(),
MeasureFormat.FormatWidth.WIDE);
if (durationSinceLastInteraction.toHours() < MIN_HOUR) {
return context.getString(R.string.last_interaction_status_less_than,
return context.getString(includeLastChatted ? R.string.last_interaction_status_less_than
: R.string.less_than_timestamp,
formatter.formatMeasures(new Measure(MIN_HOUR, MeasureUnit.HOUR)));
} else if (durationSinceLastInteraction.toDays() < ONE_DAY) {
return context.getString(R.string.last_interaction_status, formatter.formatMeasures(
new Measure(durationSinceLastInteraction.toHours(), MeasureUnit.HOUR)));
return context.getString(
includeLastChatted ? R.string.last_interaction_status : R.string.timestamp,
formatter.formatMeasures(
new Measure(durationSinceLastInteraction.toHours(), MeasureUnit.HOUR)));
} else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK) {
return context.getString(R.string.last_interaction_status, formatter.formatMeasures(
new Measure(durationSinceLastInteraction.toDays(), MeasureUnit.DAY)));
return context.getString(
includeLastChatted ? R.string.last_interaction_status : R.string.timestamp,
formatter.formatMeasures(
new Measure(durationSinceLastInteraction.toDays(), MeasureUnit.DAY)));
} else {
return context.getString(durationSinceLastInteraction.toDays() == DAYS_IN_A_WEEK
? R.string.last_interaction_status :
R.string.last_interaction_status_over,
? (includeLastChatted ? R.string.last_interaction_status :
R.string.timestamp) :
(includeLastChatted ? R.string.last_interaction_status_over
: R.string.over_timestamp),
formatter.formatMeasures(
new Measure(durationSinceLastInteraction.toDays() / DAYS_IN_A_WEEK,
MeasureUnit.WEEK)));
@@ -468,4 +519,139 @@ public class PeopleSpaceUtils {
return tile != null && tile.getUserName().length() != 0;
}
private static boolean hasBirthdayStatus(PeopleSpaceTile tile, Context context) {
return tile.getStatusText() != null && tile.getStatusText().equals(
context.getString(R.string.birthday_status));
}
/** Calls to retrieve birthdays on a background thread. */
private static void getBirthdaysOnBackgroundThread(Context context,
AppWidgetManager appWidgetManager,
Map<Integer, PeopleSpaceTile> peopleSpaceTiles, int[] appWidgetIds) {
ThreadUtils.postOnBackgroundThread(
() -> getBirthdays(context, appWidgetManager, peopleSpaceTiles, appWidgetIds));
}
/** Queries the Contacts DB for any birthdays today. */
@VisibleForTesting
public static void getBirthdays(Context context, AppWidgetManager appWidgetManager,
Map<Integer, PeopleSpaceTile> widgetIdToTile, int[] appWidgetIds) {
if (DEBUG) Log.d(TAG, "Get birthdays");
if (appWidgetIds.length == 0) return;
List<String> lookupKeysWithBirthdaysToday = getContactLookupKeysWithBirthdaysToday(context);
for (int appWidgetId : appWidgetIds) {
PeopleSpaceTile storedTile = widgetIdToTile.get(appWidgetId);
if (storedTile == null || storedTile.getContactUri() == null) {
if (DEBUG) Log.d(TAG, "No contact uri for: " + storedTile);
removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId);
continue;
}
if (lookupKeysWithBirthdaysToday.isEmpty()) {
if (DEBUG) Log.d(TAG, "No birthdays today");
removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId);
continue;
}
updateTileWithBirthday(context, appWidgetManager, lookupKeysWithBirthdaysToday,
storedTile,
appWidgetId);
}
}
/** Removes the birthday status if present in {@code storedTile} and pushes the update. */
private static void removeBirthdayStatusIfPresent(AppWidgetManager appWidgetManager,
Context context, PeopleSpaceTile storedTile, int appWidgetId) {
if (hasBirthdayStatus(storedTile, context)) {
if (DEBUG) Log.d(TAG, "Remove " + storedTile.getUserName() + "'s birthday");
updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId,
storedTile.toBuilder()
.setStatusText(null)
.build());
}
}
/**
* Update {@code storedTile} if the contact has a lookup key matched to any {@code
* lookupKeysWithBirthdays}.
*/
private static void updateTileWithBirthday(Context context, AppWidgetManager appWidgetManager,
List<String> lookupKeysWithBirthdaysToday, PeopleSpaceTile storedTile,
int appWidgetId) {
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(storedTile.getContactUri(),
null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
String storedLookupKey = cursor.getString(
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY));
if (!storedLookupKey.isEmpty() && lookupKeysWithBirthdaysToday.contains(
storedLookupKey)) {
if (DEBUG) Log.d(TAG, storedTile.getUserName() + "'s birthday today!");
updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId,
storedTile.toBuilder()
.setStatusText(context.getString(R.string.birthday_status))
.build());
return;
}
}
} catch (SQLException e) {
Log.e(TAG, "Failed to query contact: " + e);
} finally {
if (cursor != null) {
cursor.close();
}
}
removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId);
}
/** Update app widget options and the current view. */
private static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager,
Context context, int appWidgetId, PeopleSpaceTile tile) {
updateAppWidgetOptions(appWidgetManager, appWidgetId, tile);
RemoteViews views = createRemoteViews(context,
tile, appWidgetId);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
/**
* Returns lookup keys for all contacts with a birthday today.
*
* <p>Birthdays are queried from a different table within the Contacts DB than the table for
* the Contact Uri provided by most messaging apps. Matching by the contact ID is then quite
* fragile as the row IDs across the different tables are not guaranteed to stay aligned, so we
* match the data by {@link ContactsContract.ContactsColumns#LOOKUP_KEY} key to ensure proper
* matching across all the Contacts DB tables.
*/
private static List<String> getContactLookupKeysWithBirthdaysToday(Context context) {
List<String> lookupKeysWithBirthdaysToday = new ArrayList<>(1);
String today = new SimpleDateFormat("MM-dd").format(new Date());
String[] projection = new String[]{
ContactsContract.CommonDataKinds.Event.LOOKUP_KEY,
ContactsContract.CommonDataKinds.Event.START_DATE};
String where =
ContactsContract.Data.MIMETYPE
+ "= ? AND " + ContactsContract.CommonDataKinds.Event.TYPE + "="
+ ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY + " AND substr("
+ ContactsContract.CommonDataKinds.Event.START_DATE + ",6) = ?";
String[] selection =
new String[]{ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, today};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
projection, where, selection, null);
while (cursor != null && cursor.moveToNext()) {
String lookupKey = cursor.getString(
cursor.getColumnIndex(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY));
lookupKeysWithBirthdaysToday.add(lookupKey);
}
} catch (SQLException e) {
Log.e(TAG, "Failed to query birthdays: " + e);
} finally {
if (cursor != null) {
cursor.close();
}
}
return lookupKeysWithBirthdaysToday;
}
}

View File

@@ -34,7 +34,6 @@ import com.android.systemui.people.PeopleSpaceUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/** People Space Widget RemoteViewsFactory class. */
public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
@@ -45,7 +44,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
private INotificationManager mNotificationManager;
private PackageManager mPackageManager;
private LauncherApps mLauncherApps;
private List<Map.Entry<Long, PeopleSpaceTile>> mTiles = new ArrayList<>();
private List<PeopleSpaceTile> mTiles = new ArrayList<>();
private Context mContext;
public PeopleSpaceWidgetRemoteViewsFactory(Context context, Intent intent) {
@@ -100,11 +99,10 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R
RemoteViews personView = new RemoteViews(mContext.getPackageName(),
R.layout.people_space_widget_item);
try {
Map.Entry<Long, PeopleSpaceTile> entry = mTiles.get(i);
PeopleSpaceTile tile = entry.getValue();
long lastInteraction = entry.getKey();
PeopleSpaceTile tile = mTiles.get(i);
String status = PeopleSpaceUtils.getLastInteractionString(mContext, lastInteraction);
String status = PeopleSpaceUtils.getLastInteractionString(mContext,
tile.getLastInteractionTimestamp(), true);
personView.setTextViewText(R.id.status, status);
personView.setTextViewText(R.id.name, tile.getUserName().toString());

View File

@@ -21,7 +21,13 @@ import static com.android.systemui.people.PeopleSpaceUtils.OPTIONS_PEOPLE_SPACE_
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
@@ -29,11 +35,16 @@ import android.app.Person;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.database.Cursor;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
@@ -52,6 +63,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Map;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class PeopleSpaceUtilsTest extends SysuiTestCase {
@@ -61,6 +74,32 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
private static final String SHORTCUT_ID = "101";
private static final String NOTIFICATION_KEY = "notification_key";
private static final String NOTIFICATION_CONTENT = "notification_content";
private static final String TEST_LOOKUP_KEY = "lookup_key";
private static final int TEST_COLUMN_INDEX = 1;
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
private static final Person PERSON = new Person.Builder()
.setName("name")
.setKey("abc")
.setUri(URI.toString())
.setBot(false)
.build();
private static final PeopleSpaceTile PERSON_TILE =
new PeopleSpaceTile
.Builder(SHORTCUT_ID, "username", ICON, new Intent())
.setNotificationKey(NOTIFICATION_KEY)
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(URI)
.build();
private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
SHORTCUT_ID).setLongLabel(
"name").setPerson(PERSON)
.build();
private final ShortcutInfo mShortcutInfoWithoutPerson = new ShortcutInfo.Builder(mContext,
SHORTCUT_ID).setLongLabel(
"name")
.build();
@Mock
private NotificationListener mListenerService;
@@ -68,26 +107,12 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
private IAppWidgetService mIAppWidgetService;
@Mock
private AppWidgetManager mAppWidgetManager;
private static Icon sIcon = Icon.createWithResource("package", R.drawable.ic_android);
private static Uri sUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority("something")
.path("test")
.build();
private static Person sPerson = new Person.Builder()
.setName("name")
.setKey("abc")
.setUri("uri")
.setBot(false)
.build();
private static PeopleSpaceTile sPeopleSpaceTile =
new PeopleSpaceTile
.Builder(SHORTCUT_ID, "username", sIcon, new Intent())
.setNotificationKey(NOTIFICATION_KEY)
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(sUri)
.build();
@Mock
private Cursor mMockCursor;
@Mock
private ContentResolver mMockContentResolver;
@Mock
private Context mMockContext;
@Before
public void setUp() throws RemoteException {
@@ -97,13 +122,19 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
Bundle options = new Bundle();
options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, sPeopleSpaceTile);
options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
.thenReturn(options);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
.thenReturn(new Bundle());
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
when(mMockContentResolver.query(any(Uri.class), any(), anyString(), any(),
isNull())).thenReturn(mMockCursor);
when(mMockContext.getString(R.string.birthday_status)).thenReturn(
mContext.getString(R.string.birthday_status));
}
@Test
@@ -218,10 +249,10 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
.setShortcutId(SHORTCUT_ID)
.setStyle(new Notification.MessagingStyle(sPerson)
.addMessage(new Notification.MessagingStyle.Message("text1", 0, sPerson))
.addMessage(new Notification.MessagingStyle.Message("text2", 20, sPerson))
.addMessage(new Notification.MessagingStyle.Message("text3", 10, sPerson))
.setStyle(new Notification.MessagingStyle(PERSON)
.addMessage(new Notification.MessagingStyle.Message("text1", 0, PERSON))
.addMessage(new Notification.MessagingStyle.Message("text2", 20, PERSON))
.addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
)
.build();
StatusBarNotification sbn = new SbnBuilder()
@@ -238,21 +269,21 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
public void testAugmentTileFromStorageWithNotification() {
PeopleSpaceTile tile =
new PeopleSpaceTile
.Builder("id", "userName", sIcon, new Intent())
.Builder("id", "userName", ICON, new Intent())
.build();
PeopleSpaceTile actual = PeopleSpaceUtils
.augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITH_SHORTCUT);
assertThat(actual.getNotificationKey()).isEqualTo(NOTIFICATION_KEY);
assertThat(actual.getNotificationContent()).isEqualTo(NOTIFICATION_CONTENT);
assertThat(actual.getNotificationDataUri()).isEqualTo(sUri);
assertThat(actual.getNotificationDataUri()).isEqualTo(URI);
}
@Test
public void testAugmentTileFromStorageWithoutNotification() {
PeopleSpaceTile tile =
new PeopleSpaceTile
.Builder("id", "userName", sIcon, new Intent())
.Builder("id", "userName", ICON, new Intent())
.build();
PeopleSpaceTile actual = PeopleSpaceUtils
.augmentTileFromStorage(tile, mAppWidgetManager, WIDGET_ID_WITHOUT_SHORTCUT);
@@ -261,4 +292,98 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
assertThat(actual.getNotificationKey()).isEqualTo(null);
assertThat(actual.getNotificationDataUri()).isEqualTo(null);
}
@Test
public void testDoNotUpdateSingleConversationAppWidgetWhenNotBirthday() {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
when(mMockCursor.moveToNext()).thenReturn(true, false);
when(mMockCursor.getString(eq(TEST_COLUMN_INDEX))).thenReturn(TEST_LOOKUP_KEY);
when(mMockCursor.getColumnIndex(eq(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)
)).thenReturn(TEST_COLUMN_INDEX);
// Existing tile does not have birthday status.
Map<Integer, PeopleSpaceTile> widgetIdToTile = Map.of(WIDGET_ID_WITH_SHORTCUT,
new PeopleSpaceTile.Builder(mShortcutInfoWithoutPerson,
mContext.getSystemService(LauncherApps.class)).build());
PeopleSpaceUtils.getBirthdays(mMockContext, mAppWidgetManager,
widgetIdToTile, widgetIdsArray);
verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
@Test
public void testUpdateSingleConversationAppWidgetWithoutPersonContactUriToRemoveBirthday() {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
when(mMockCursor.moveToNext()).thenReturn(true, false);
when(mMockCursor.getString(eq(TEST_COLUMN_INDEX))).thenReturn(TEST_LOOKUP_KEY);
when(mMockCursor.getColumnIndex(eq(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)
)).thenReturn(TEST_COLUMN_INDEX);
// Existing tile has a birthday status.
Map<Integer, PeopleSpaceTile> widgetIdToTile = Map.of(WIDGET_ID_WITH_SHORTCUT,
new PeopleSpaceTile.Builder(mShortcutInfoWithoutPerson,
mContext.getSystemService(LauncherApps.class)).setStatusText(
mContext.getString(R.string.birthday_status)).build());
PeopleSpaceUtils.getBirthdays(mMockContext, mAppWidgetManager,
widgetIdToTile, widgetIdsArray);
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
@Test
public void testUpdateSingleConversationAppWidgetToRemoveBirthdayWhenNoLongerBirthday() {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
Cursor mockPersonUriCursor = mock(Cursor.class);
Cursor mockBirthdaysUriCursor = mock(Cursor.class);
when(mockPersonUriCursor.moveToNext()).thenReturn(true, false);
when(mockBirthdaysUriCursor.moveToNext()).thenReturn(true, false);
when(mockBirthdaysUriCursor.getColumnIndex(
eq(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)
)).thenReturn(TEST_COLUMN_INDEX);
when(mockPersonUriCursor.getColumnIndex(
eq(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)
)).thenReturn(TEST_COLUMN_INDEX);
// Return different cursors based on the Uri queried.
when(mMockContentResolver.query(eq(URI), any(), any(), any(),
any())).thenReturn(mockPersonUriCursor);
when(mMockContentResolver.query(eq(ContactsContract.Data.CONTENT_URI), any(), any(), any(),
any())).thenReturn(mockBirthdaysUriCursor);
// Each cursor returns a different lookup key.
when(mockBirthdaysUriCursor.getString(eq(TEST_COLUMN_INDEX))).thenReturn(TEST_LOOKUP_KEY);
when(mockPersonUriCursor.getString(eq(TEST_COLUMN_INDEX))).thenReturn(
TEST_LOOKUP_KEY + "differentlookup");
// Existing tile has a birthday status.
Map<Integer, PeopleSpaceTile> widgetIdToTile = Map.of(WIDGET_ID_WITH_SHORTCUT,
new PeopleSpaceTile.Builder(mShortcutInfo,
mContext.getSystemService(LauncherApps.class)).setStatusText(
mContext.getString(R.string.birthday_status)).build());
PeopleSpaceUtils.getBirthdays(mMockContext, mAppWidgetManager,
widgetIdToTile, widgetIdsArray);
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
@Test
public void testUpdateSingleConversationAppWidgetWhenBirthday() {
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT};
when(mMockCursor.moveToNext()).thenReturn(true, false, true, false);
when(mMockCursor.getString(eq(TEST_COLUMN_INDEX))).thenReturn(TEST_LOOKUP_KEY);
when(mMockCursor.getColumnIndex(eq(ContactsContract.CommonDataKinds.Event.LOOKUP_KEY)
)).thenReturn(TEST_COLUMN_INDEX);
// Existing tile has a birthday status.
Map<Integer, PeopleSpaceTile> widgetIdToTile = Map.of(WIDGET_ID_WITH_SHORTCUT,
new PeopleSpaceTile.Builder(mShortcutInfo,
mContext.getSystemService(LauncherApps.class)).setStatusText(
mContext.getString(R.string.birthday_status)).build());
PeopleSpaceUtils.getBirthdays(mMockContext, mAppWidgetManager,
widgetIdToTile, widgetIdsArray);
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any());
}
}

View File

@@ -37,7 +37,6 @@ import android.app.NotificationChannel;
import android.app.Person;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ParceledListSlice;
@@ -95,6 +94,21 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private static final String OTHER_SHORTCUT_ID = "102";
private static final String NOTIFICATION_KEY = "notification_key";
private static final String NOTIFICATION_CONTENT = "notification_content";
private static final Uri URI = Uri.parse("fake_uri");
private static final Icon ICON = Icon.createWithResource("package", R.drawable.ic_android);
private static final Person PERSON = new Person.Builder()
.setName("name")
.setKey("abc")
.setUri(URI.toString())
.setBot(false)
.build();
private static final PeopleSpaceTile PERSON_TILE =
new PeopleSpaceTile
.Builder(SHORTCUT_ID, "username", ICON, new Intent())
.setNotificationKey(NOTIFICATION_KEY)
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(URI)
.build();
private PeopleSpaceWidgetManager mManager;
@@ -110,26 +124,6 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<NotificationHandler> mListenerCaptor;
private static Icon sIcon = Icon.createWithResource("package", R.drawable.ic_android);
private static Uri sUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority("something")
.path("test")
.build();
private static Person sPerson = new Person.Builder()
.setName("name")
.setKey("abc")
.setUri("uri")
.setBot(false)
.build();
private static PeopleSpaceTile sPeopleSpaceTile =
new PeopleSpaceTile
.Builder(SHORTCUT_ID, "username", sIcon, new Intent())
.setNotificationKey(NOTIFICATION_KEY)
.setNotificationContent(NOTIFICATION_CONTENT)
.setNotificationDataUri(sUri)
.build();
private final NoManSimulator mNoMan = new NoManSimulator();
private final FakeSystemClock mClock = new FakeSystemClock();
@@ -150,9 +144,9 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sp.edit();
editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID);
editor.commit();
editor.apply();
Bundle options = new Bundle();
options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, sPeopleSpaceTile);
options.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, PERSON_TILE);
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITH_SHORTCUT)))
.thenReturn(options);
@@ -304,7 +298,6 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mIAppWidgetService, never()).getAppWidgetIds(any());
verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
any(RemoteViews.class));
@@ -456,8 +449,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
.setContentTitle("TEST_TITLE")
.setContentText("TEST_TEXT")
.setShortcutId(shortcutId)
.setStyle(new Notification.MessagingStyle(sPerson)
.addMessage(new Notification.MessagingStyle.Message("text3", 10, sPerson))
.setStyle(new Notification.MessagingStyle(PERSON)
.addMessage(new Notification.MessagingStyle.Message("text3", 10, PERSON))
)
.build();
return new SbnBuilder()