diff --git a/packages/SystemUI/res/drawable/ic_cake.xml b/packages/SystemUI/res/drawable/ic_cake.xml new file mode 100644 index 0000000000000..9c83b43ab057e --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_cake.xml @@ -0,0 +1,24 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_celebration.xml b/packages/SystemUI/res/drawable/ic_celebration.xml new file mode 100644 index 0000000000000..10fe4066994c2 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_celebration.xml @@ -0,0 +1,37 @@ + + + + + + + + + diff --git a/packages/SystemUI/res/drawable/ic_gift.xml b/packages/SystemUI/res/drawable/ic_gift.xml new file mode 100644 index 0000000000000..fab36c3ce0dfc --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_gift.xml @@ -0,0 +1,24 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_pages.xml b/packages/SystemUI/res/drawable/ic_pages.xml new file mode 100644 index 0000000000000..9cd076d106316 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_pages.xml @@ -0,0 +1,24 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_play_games.xml b/packages/SystemUI/res/drawable/ic_play_games.xml new file mode 100644 index 0000000000000..20096f45b812b --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_play_games.xml @@ -0,0 +1,24 @@ + + + + diff --git a/packages/SystemUI/res/drawable/ic_video.xml b/packages/SystemUI/res/drawable/ic_video.xml new file mode 100644 index 0000000000000..3668338a0e438 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_video.xml @@ -0,0 +1,24 @@ + + + + diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml index 4994c696e9022..e294dad6ce34c 100644 --- a/packages/SystemUI/res/layout/people_tile_large_with_content.xml +++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml @@ -34,8 +34,6 @@ @@ -73,12 +71,12 @@ diff --git a/packages/SystemUI/res/layout/people_tile_medium_empty.xml b/packages/SystemUI/res/layout/people_tile_medium_empty.xml index bebc872be5141..8b2fddc5c9272 100644 --- a/packages/SystemUI/res/layout/people_tile_medium_empty.xml +++ b/packages/SystemUI/res/layout/people_tile_medium_empty.xml @@ -27,19 +27,23 @@ android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> - - + + + + @@ -112,7 +110,8 @@ android:clipToOutline="true"> diff --git a/packages/SystemUI/res/layout/people_tile_small.xml b/packages/SystemUI/res/layout/people_tile_small.xml index 4e5c04cb5ad6a..7a1371d034b6d 100644 --- a/packages/SystemUI/res/layout/people_tile_small.xml +++ b/packages/SystemUI/res/layout/people_tile_small.xml @@ -39,7 +39,7 @@ diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java index f3cb35959ed45..96aeb60ae93c5 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java @@ -29,6 +29,8 @@ import android.graphics.drawable.Drawable; import android.util.IconDrawableFactory; import android.util.Log; +import androidx.core.graphics.drawable.RoundedBitmapDrawable; + import com.android.settingslib.Utils; import com.android.systemui.R; @@ -58,7 +60,7 @@ class PeopleStoryIconFactory implements AutoCloseable { mIconDrawableFactory = iconDrawableFactory; mImportantConversationColor = context.getColor(R.color.important_conversation); mAccentColor = Utils.getColorAttr(context, - com.android.internal.R.attr.colorAccentPrimary).getDefaultColor(); + com.android.internal.R.attr.colorAccentPrimaryVariant).getDefaultColor(); mContext = context; } @@ -83,7 +85,8 @@ class PeopleStoryIconFactory implements AutoCloseable { * Returns a {@link Drawable} for the entire conversation. The shortcut icon will be badged * with the launcher icon of the app specified by packageName. */ - public Drawable getPeopleTileDrawable(Drawable headDrawable, String packageName, int userId, + public Drawable getPeopleTileDrawable(RoundedBitmapDrawable headDrawable, String packageName, + int userId, boolean important, boolean newStory) { return new PeopleStoryIconDrawable(headDrawable, getAppBadge(packageName, userId), mIconBitmapSize, mImportantConversationColor, important, mIconSize, mDensity, @@ -96,7 +99,7 @@ class PeopleStoryIconFactory implements AutoCloseable { */ public static class PeopleStoryIconDrawable extends Drawable { private float mFullIconSize; - private Drawable mAvatar; + private RoundedBitmapDrawable mAvatar; private Drawable mBadgeIcon; private int mIconSize; private Paint mPriorityRingPaint; @@ -105,12 +108,13 @@ class PeopleStoryIconFactory implements AutoCloseable { private Paint mStoryPaint; private float mDensity; - PeopleStoryIconDrawable(Drawable avatar, + PeopleStoryIconDrawable(RoundedBitmapDrawable avatar, Drawable badgeIcon, int iconSize, @ColorInt int ringColor, boolean showImportantRing, float fullIconSize, float density, @ColorInt int accentColor, boolean showStoryRing) { + avatar.setCircular(true); mAvatar = avatar; mBadgeIcon = badgeIcon; mIconSize = iconSize; diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index 59c7fd197c8b9..72d382ac0799b 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -45,9 +45,6 @@ import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; -import android.icu.text.MeasureFormat; -import android.icu.util.Measure; -import android.icu.util.MeasureUnit; import android.net.Uri; import android.os.Bundle; import android.os.UserHandle; @@ -61,6 +58,9 @@ import android.view.View; import android.widget.RemoteViews; import android.widget.TextView; +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; + import com.android.internal.annotations.VisibleForTesting; import com.android.launcher3.icons.FastBitmapDrawable; import com.android.systemui.R; @@ -72,6 +72,7 @@ import java.text.NumberFormat; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -94,6 +95,8 @@ public class PeopleTileViewHelper { public static final int LAYOUT_LARGE = 2; private static final int MIN_CONTENT_MAX_LINES = 2; + private static final int NAME_MAX_LINES_WITHOUT_LAST_INTERACTION = 3; + private static final int NAME_MAX_LINES_WITH_LAST_INTERACTION = 1; private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_NOTIF_CONTENT = 16 + 22 + 8 + 16; private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_STATUS_CONTENT = 16 + 16 + 24 + 4 + 16; @@ -225,7 +228,9 @@ public class PeopleTileViewHelper { Log.d(TAG, "Create status view for: " + statusesForEntireView.get(0).getActivity()); } - return createStatusRemoteViews(statusesForEntireView.get(0)); + ConversationStatus mostRecentlyStartedStatus = statusesForEntireView.stream().max( + Comparator.comparing(s -> s.getStartTimeMillis())).get(); + return createStatusRemoteViews(mostRecentlyStartedStatus); } return createLastInteractionRemoteViews(); @@ -579,11 +584,33 @@ public class PeopleTileViewHelper { setMaxLines(views, false); } setAvailabilityDotPadding(views, R.dimen.availability_dot_status_padding); - // TODO: Set status pre-defined icons - views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person); + views.setImageViewResource(R.id.predefined_icon, getDrawableForStatus(status)); return views; } + private int getDrawableForStatus(ConversationStatus status) { + switch (status.getActivity()) { + case ACTIVITY_NEW_STORY: + return R.drawable.ic_pages; + case ACTIVITY_ANNIVERSARY: + return R.drawable.ic_celebration; + case ACTIVITY_UPCOMING_BIRTHDAY: + return R.drawable.ic_gift; + case ACTIVITY_BIRTHDAY: + return R.drawable.ic_cake; + case ACTIVITY_LOCATION: + return R.drawable.ic_location; + case ACTIVITY_GAME: + return R.drawable.ic_play_games; + case ACTIVITY_VIDEO: + return R.drawable.ic_video; + case ACTIVITY_AUDIO: + return R.drawable.ic_music_note; + default: + return R.drawable.ic_person; + } + } + /** * Update the padding of the availability dot. The padding on the availability dot decreases * on the status layouts compared to all other layouts. @@ -658,7 +685,6 @@ public class PeopleTileViewHelper { } private RemoteViews decorateBackground(RemoteViews views, CharSequence content) { - int visibility = View.GONE; CharSequence emoji = getDoubleEmoji(content); if (!TextUtils.isEmpty(emoji)) { setEmojiBackground(views, emoji); @@ -768,6 +794,7 @@ public class PeopleTileViewHelper { } private RemoteViews setViewForContentLayout(RemoteViews views) { + views = decorateBackground(views, ""); if (mLayoutSize == LAYOUT_SMALL) { views.setViewVisibility(R.id.predefined_icon, View.VISIBLE); views.setViewVisibility(R.id.name, View.GONE); @@ -819,6 +846,7 @@ public class PeopleTileViewHelper { private RemoteViews createLastInteractionRemoteViews() { RemoteViews views = new RemoteViews(mContext.getPackageName(), getEmptyLayout()); + views.setInt(R.id.name, "setMaxLines", NAME_MAX_LINES_WITH_LAST_INTERACTION); if (mLayoutSize == LAYOUT_SMALL) { views.setViewVisibility(R.id.name, View.VISIBLE); views.setViewVisibility(R.id.predefined_icon, View.GONE); @@ -836,6 +864,9 @@ public class PeopleTileViewHelper { } else { if (DEBUG) Log.d(TAG, "Hide last interaction"); views.setViewVisibility(R.id.last_interaction, View.GONE); + if (mLayoutSize == LAYOUT_MEDIUM) { + views.setInt(R.id.name, "setMaxLines", NAME_MAX_LINES_WITHOUT_LAST_INTERACTION); + } } return views; } @@ -891,8 +922,9 @@ public class PeopleTileViewHelper { context.getPackageManager(), IconDrawableFactory.newInstance(context, false), maxAvatarSize); - Drawable drawable = icon.loadDrawable(context); - Drawable personDrawable = storyIcon.getPeopleTileDrawable(drawable, + RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create( + context.getResources(), icon.getBitmap()); + Drawable personDrawable = storyIcon.getPeopleTileDrawable(roundedDrawable, tile.getPackageName(), getUserId(tile), tile.isImportantConversation(), hasNewStory); return convertDrawableToBitmap(personDrawable); @@ -907,14 +939,11 @@ public class PeopleTileViewHelper { } long now = System.currentTimeMillis(); Duration durationSinceLastInteraction = Duration.ofMillis(now - lastInteraction); - MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(), - MeasureFormat.FormatWidth.WIDE); if (durationSinceLastInteraction.toDays() <= ONE_DAY) { return null; } else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK) { - return context.getString(R.string.days_timestamp, formatter.formatMeasures( - new Measure(durationSinceLastInteraction.toDays(), - MeasureUnit.DAY))); + return context.getString(R.string.days_timestamp, + durationSinceLastInteraction.toDays()); } else if (durationSinceLastInteraction.toDays() == DAYS_IN_A_WEEK) { return context.getString(R.string.one_week_timestamp); } else if (durationSinceLastInteraction.toDays() < DAYS_IN_A_WEEK * 2) { diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 9e0dd72e77f54..2602d7a3e369a 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -667,12 +667,16 @@ public class PeopleSpaceWidgetManager { if (icon != null) { updatedTile.setUserIcon(icon); } + if (DEBUG) Log.d(TAG, "Statuses: " + conversation.getStatuses().toString()); + NotificationChannel channel = conversation.getParentNotificationChannel(); + if (channel != null) { + if (DEBUG) Log.d(TAG, "Important:" + channel.isImportantConversation()); + updatedTile.setIsImportantConversation(channel.isImportantConversation()); + } updatedTile .setContactUri(uri) .setStatuses(conversation.getStatuses()) - .setLastInteractionTimestamp(conversation.getLastEventTimestamp()) - .setIsImportantConversation(conversation.getParentNotificationChannel() != null - && conversation.getParentNotificationChannel().isImportantConversation()); + .setLastInteractionTimestamp(conversation.getLastEventTimestamp()); updateAppWidgetOptionsAndView(appWidgetId, updatedTile.build()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java index 3b56f225ba046..67505c42ef9a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java @@ -69,6 +69,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.time.Duration; import java.util.Arrays; @RunWith(AndroidTestingRunner.class) @@ -184,6 +185,30 @@ public class PeopleTileViewHelperTest extends SysuiTestCase { assertEquals(View.GONE, largeResult.findViewById(R.id.last_interaction).getVisibility()); } + @Test + public void testLastInteractionTime() { + long now = System.currentTimeMillis(); + long fiveDaysAgo = now - Duration.ofDays(5).toMillis(); + String lastInteractionString = PeopleTileViewHelper.getLastInteractionString(mContext, + fiveDaysAgo); + assertThat(lastInteractionString).isEqualTo("5 days ago"); + + long lessThanOneDayAgo = now - Duration.ofHours(20).toMillis(); + lastInteractionString = PeopleTileViewHelper.getLastInteractionString(mContext, + lessThanOneDayAgo); + assertThat(lastInteractionString).isNull(); + + long overOneWeekAgo = now - Duration.ofDays(8).toMillis(); + lastInteractionString = PeopleTileViewHelper.getLastInteractionString(mContext, + overOneWeekAgo); + assertThat(lastInteractionString).isEqualTo("Over 1 week ago"); + + long overTwoWeeksAgo = now - Duration.ofDays(15).toMillis(); + lastInteractionString = PeopleTileViewHelper.getLastInteractionString(mContext, + overTwoWeeksAgo); + assertThat(lastInteractionString).isEqualTo("Over 2 weeks ago"); + } + @Test public void testCreateRemoteViewsWithLastInteractionTime() { PeopleSpaceTile tileWithLastInteraction = diff --git a/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java index c631026f43891..49d5e50e03450 100644 --- a/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java +++ b/services/people/java/com/android/server/people/data/ConversationStatusExpirationBroadcastReceiver.java @@ -20,26 +20,18 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.PendingIntent; -import android.app.job.JobInfo; -import android.app.job.JobParameters; -import android.app.job.JobScheduler; -import android.app.job.JobService; import android.app.people.ConversationStatus; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; +import android.os.Binder; import android.os.CancellationSignal; -import android.os.SystemClock; import com.android.server.LocalServices; -import com.android.server.notification.NotificationRecord; import com.android.server.people.PeopleServiceInternal; -import java.util.concurrent.TimeUnit; - /** * If a {@link ConversationStatus} is added to the system with an expiration time, remove that * status at that time @@ -53,18 +45,22 @@ public class ConversationStatusExpirationBroadcastReceiver extends BroadcastRece void scheduleExpiration(Context context, @UserIdInt int userId, String pkg, String conversationId, ConversationStatus status) { - - final PendingIntent pi = PendingIntent.getBroadcast(context, - REQUEST_CODE, - new Intent(ACTION) - .setData(new Uri.Builder().scheme(SCHEME) - .appendPath(getKey(userId, pkg, conversationId, status)) - .build()) - .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) - .putExtra(EXTRA_USER_ID, userId), - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - context.getSystemService(AlarmManager.class).setExactAndAllowWhileIdle( - AlarmManager.RTC_WAKEUP, status.getEndTimeMillis(), pi); + final long identity = Binder.clearCallingIdentity(); + try { + final PendingIntent pi = PendingIntent.getBroadcast(context, + REQUEST_CODE, + new Intent(ACTION) + .setData(new Uri.Builder().scheme(SCHEME) + .appendPath(getKey(userId, pkg, conversationId, status)) + .build()) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) + .putExtra(EXTRA_USER_ID, userId), + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + context.getSystemService(AlarmManager.class).setExactAndAllowWhileIdle( + AlarmManager.RTC_WAKEUP, status.getEndTimeMillis(), pi); + } finally { + Binder.restoreCallingIdentity(identity); + } } private static String getKey(@UserIdInt int userId, String pkg,