Implement 2x2 layout and text resizing
Expand text to best-fit the layout by calculating maxLines for medium and large layout. Bug: 182377480 Test: PeopleSpaceUtils Change-Id: I980ef776dfdf6e3007b8761ec7e1db3ec5fe5b7d
This commit is contained in:
67
packages/SystemUI/res/layout/people_tile_large_empty.xml
Normal file
67
packages/SystemUI/res/layout/people_tile_large_empty.xml
Normal file
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (C) 2021 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.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/people_space_tile_view_card"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/person_icon"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_gravity="center"
|
||||
android:paddingTop="14dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="16sp" />
|
||||
<TextView
|
||||
android:id="@+id/last_interaction"
|
||||
android:layout_gravity="center"
|
||||
android:text="@string/empty_status"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
|
||||
android:textSize="14sp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"/>
|
||||
<ImageView
|
||||
android:id="@+id/availability"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="26dp"
|
||||
android:paddingTop="16dp"
|
||||
android:background="@drawable/circle_green_10dp"/>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
109
packages/SystemUI/res/layout/people_tile_large_with_content.xml
Normal file
109
packages/SystemUI/res/layout/people_tile_large_with_content.xml
Normal file
@@ -0,0 +1,109 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (C) 2021 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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/item"
|
||||
android:background="@drawable/people_space_tile_view_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:padding="16dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start|top"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/person_icon"
|
||||
android:layout_marginStart="-2dp"
|
||||
android:layout_marginTop="-2dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/availability"
|
||||
android:layout_marginStart="-2dp"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:background="@drawable/circle_green_10dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_gravity="center"
|
||||
android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="12dp"
|
||||
android:gravity="start"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:text="@string/empty_user_name"
|
||||
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="4dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/predefined_icon"
|
||||
android:gravity="start|center_vertical"
|
||||
android:paddingEnd="6dp"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="18dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_gravity="center"
|
||||
android:id="@+id/subtext"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:text="@string/empty_user_name"
|
||||
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/people_space_content_background"
|
||||
android:gravity="center"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<TextView
|
||||
android:layout_gravity="center"
|
||||
android:id="@+id/text_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:singleLine="false"
|
||||
android:text="@string/empty_status"
|
||||
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
@@ -35,16 +35,17 @@
|
||||
android:layout_height="match_parent">
|
||||
<ImageView
|
||||
android:id="@+id/person_icon"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"/>
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"/>
|
||||
<ImageView
|
||||
android:id="@+id/availability"
|
||||
android:layout_marginStart="-2dp"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:background="@drawable/circle_green_10dp"/>
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingStart="6dp"
|
||||
android:gravity="top"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
@@ -27,28 +27,37 @@
|
||||
android:layout_gravity="center"
|
||||
android:padding="8dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:gravity="top"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<ImageView
|
||||
android:gravity="start"
|
||||
android:id="@+id/person_icon"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="56dp"/>
|
||||
android:layout_height="0dp">
|
||||
|
||||
<ImageView
|
||||
android:gravity="start"
|
||||
android:id="@+id/person_icon"
|
||||
android:layout_marginStart="-2dp"
|
||||
android:layout_marginTop="-2dp"
|
||||
android:layout_width="52dp"
|
||||
android:layout_height="52dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/availability"
|
||||
android:layout_marginStart="-2dp"
|
||||
android:layout_width="10dp"
|
||||
android:layout_height="10dp"
|
||||
android:background="@drawable/circle_green_10dp"/>
|
||||
android:background="@drawable/circle_green_10dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:gravity="top|start"
|
||||
android:paddingStart="12dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subtext"
|
||||
android:text="@string/empty_user_name"
|
||||
@@ -58,38 +67,36 @@
|
||||
android:paddingBottom="4dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/content_background"
|
||||
android:background="@drawable/people_space_content_background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/people_space_content_background"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_content"
|
||||
android:text="@string/empty_status"
|
||||
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="centerCrop"/>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:id="@+id/text_content"
|
||||
android:text="@string/empty_status"
|
||||
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="12sp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="2"
|
||||
android:singleLine="false"
|
||||
android:ellipsize="end"/>
|
||||
android:maxLines="2"
|
||||
android:singleLine="false"
|
||||
android:ellipsize="end" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:gravity="bottom"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="4dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
@@ -101,16 +108,17 @@
|
||||
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="14sp"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/predefined_icon"
|
||||
android:gravity="end"
|
||||
android:gravity="end|center_vertical"
|
||||
android:paddingStart="6dp"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="18dp"/>
|
||||
android:layout_height="18dp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -25,7 +25,8 @@
|
||||
android:background="@drawable/people_space_tile_view_card"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="4dp"
|
||||
android:paddingVertical="8dp">
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/person_icon"
|
||||
@@ -51,7 +52,7 @@
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingHorizontal="4dp"
|
||||
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="14sp" />
|
||||
@@ -1355,6 +1355,18 @@
|
||||
<dimen name="people_space_widget_radius">28dp</dimen>
|
||||
<dimen name="people_space_image_radius">20dp</dimen>
|
||||
<dimen name="people_space_widget_background_padding">6dp</dimen>
|
||||
<dimen name="required_width_for_medium">146dp</dimen>
|
||||
<dimen name="required_width_for_large">138dp</dimen>
|
||||
<dimen name="required_height_for_large">182dp</dimen>
|
||||
<dimen name="default_width">146dp</dimen>
|
||||
<dimen name="default_height">92dp</dimen>
|
||||
<dimen name="avatar_size_for_medium">52dp</dimen>
|
||||
<dimen name="max_people_avatar_size_for_large_content">64dp</dimen>
|
||||
<dimen name="max_people_avatar_size">108dp</dimen>
|
||||
<dimen name="name_text_size_for_small">14sp</dimen>
|
||||
<dimen name="name_text_size_for_large">24sp</dimen>
|
||||
<dimen name="content_text_size_for_medium">12sp</dimen>
|
||||
<dimen name="content_text_size_for_large">14sp</dimen>
|
||||
|
||||
<!-- Accessibility floating menu -->
|
||||
<dimen name="accessibility_floating_menu_elevation">5dp</dimen>
|
||||
|
||||
@@ -18,42 +18,23 @@ package com.android.systemui.people;
|
||||
|
||||
import static android.app.Notification.CATEGORY_MISSED_CALL;
|
||||
import static android.app.Notification.EXTRA_MESSAGES;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_AUDIO;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_LOCATION;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_UPCOMING_BIRTHDAY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_VIDEO;
|
||||
import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE;
|
||||
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
|
||||
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH;
|
||||
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT;
|
||||
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.INotificationManager;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.people.ConversationChannel;
|
||||
import android.app.people.ConversationStatus;
|
||||
import android.app.people.IPeopleManager;
|
||||
import android.app.people.PeopleSpaceTile;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
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;
|
||||
@@ -66,10 +47,7 @@ import android.provider.ContactsContract;
|
||||
import android.service.notification.ConversationChannelWrapper;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.text.TextUtils;
|
||||
import android.util.IconDrawableFactory;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
@@ -81,8 +59,6 @@ import com.android.internal.util.ArrayUtils;
|
||||
import com.android.settingslib.utils.ThreadUtils;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.people.widget.AppWidgetOptionsHelper;
|
||||
import com.android.systemui.people.widget.LaunchConversationActivity;
|
||||
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
|
||||
import com.android.systemui.people.widget.PeopleTileKey;
|
||||
import com.android.systemui.statusbar.notification.NotificationEntryManager;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
@@ -99,10 +75,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -123,18 +96,6 @@ public class PeopleSpaceUtils {
|
||||
|
||||
public static final PeopleTileKey EMPTY_KEY =
|
||||
new PeopleTileKey(EMPTY_STRING, INVALID_USER_ID, EMPTY_STRING);
|
||||
public static final int SMALL_LAYOUT = 0;
|
||||
public static final int MEDIUM_LAYOUT = 1;
|
||||
@VisibleForTesting
|
||||
static final int REQUIRED_WIDTH_FOR_MEDIUM = 146;
|
||||
private static final int AVATAR_SIZE_FOR_MEDIUM = 56;
|
||||
private static final int DEFAULT_WIDTH = 146;
|
||||
private static final int DEFAULT_HEIGHT = 92;
|
||||
|
||||
private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
|
||||
private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
|
||||
private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
|
||||
private static final Pattern MIXED_MARK_PATTERN = Pattern.compile("![?].*|.*[?]!");
|
||||
|
||||
/** Represents whether {@link StatusBarNotification} was posted or removed. */
|
||||
public enum NotificationAction {
|
||||
@@ -336,363 +297,6 @@ public class PeopleSpaceUtils {
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Creates a {@link RemoteViews} for {@code tile}. */
|
||||
public static RemoteViews createRemoteViews(Context context,
|
||||
PeopleSpaceTile tile, int appWidgetId, Bundle options) {
|
||||
int layoutSize = getLayoutSize(context, options);
|
||||
RemoteViews viewsForTile = getViewForTile(context, tile, layoutSize);
|
||||
int maxAvatarSize = getMaxAvatarSize(context, options, layoutSize);
|
||||
RemoteViews views = setCommonRemoteViewsFields(context, viewsForTile, tile, maxAvatarSize);
|
||||
return setLaunchIntents(context, views, tile, appWidgetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* The prioritization for the {@code tile} content is missed calls, followed by notification
|
||||
* content, then birthdays, then the most recent status, and finally last interaction.
|
||||
*/
|
||||
private static RemoteViews getViewForTile(Context context, PeopleSpaceTile tile,
|
||||
int layoutSize) {
|
||||
if (Objects.equals(tile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
|
||||
if (DEBUG) Log.d(TAG, "Create missed call view");
|
||||
return createMissedCallRemoteViews(context, tile, layoutSize);
|
||||
}
|
||||
|
||||
if (tile.getNotificationKey() != null) {
|
||||
if (DEBUG) Log.d(TAG, "Create notification view");
|
||||
return createNotificationRemoteViews(context, tile, layoutSize);
|
||||
}
|
||||
|
||||
// TODO: Add sorting when we expose timestamp of statuses.
|
||||
List<ConversationStatus> statusesForEntireView =
|
||||
tile.getStatuses() == null ? Arrays.asList() : tile.getStatuses().stream().filter(
|
||||
c -> isStatusValidForEntireStatusView(c)).collect(Collectors.toList());
|
||||
ConversationStatus birthdayStatus = getBirthdayStatus(tile, statusesForEntireView);
|
||||
if (birthdayStatus != null) {
|
||||
if (DEBUG) Log.d(TAG, "Create birthday view");
|
||||
return createStatusRemoteViews(context, birthdayStatus, layoutSize);
|
||||
}
|
||||
|
||||
if (!statusesForEntireView.isEmpty()) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG,
|
||||
"Create status view for: " + statusesForEntireView.get(0).getActivity());
|
||||
}
|
||||
return createStatusRemoteViews(context, statusesForEntireView.get(0), layoutSize);
|
||||
}
|
||||
|
||||
return createLastInteractionRemoteViews(context, tile, layoutSize);
|
||||
}
|
||||
|
||||
/** Calculates the best layout relative to the size in {@code options}. */
|
||||
private static int getLayoutSize(Context context, Bundle options) {
|
||||
int display = context.getResources().getConfiguration().orientation;
|
||||
int width = display == Configuration.ORIENTATION_PORTRAIT
|
||||
? options.getInt(OPTION_APPWIDGET_MIN_WIDTH, DEFAULT_WIDTH) : options.getInt(
|
||||
OPTION_APPWIDGET_MAX_WIDTH, DEFAULT_WIDTH);
|
||||
int height = display == Configuration.ORIENTATION_PORTRAIT ? options.getInt(
|
||||
OPTION_APPWIDGET_MAX_HEIGHT, DEFAULT_HEIGHT)
|
||||
: options.getInt(OPTION_APPWIDGET_MIN_HEIGHT, DEFAULT_HEIGHT);
|
||||
// Small layout used below a certain minimum width with any height.
|
||||
if (width < REQUIRED_WIDTH_FOR_MEDIUM) {
|
||||
if (DEBUG) Log.d(TAG, "Small view for width: " + width + " height: " + height);
|
||||
return SMALL_LAYOUT;
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "Medium view for width: " + width + " height: " + height);
|
||||
return MEDIUM_LAYOUT;
|
||||
}
|
||||
|
||||
/** Returns the max avatar size for {@code layoutSize} under the current {@code options}. */
|
||||
private static int getMaxAvatarSize(Context context, Bundle options, int layoutSize) {
|
||||
int avatarHeightSpace = AVATAR_SIZE_FOR_MEDIUM;
|
||||
int avatarWidthSpace = AVATAR_SIZE_FOR_MEDIUM;
|
||||
|
||||
if (layoutSize == SMALL_LAYOUT) {
|
||||
int display = context.getResources().getConfiguration().orientation;
|
||||
int width = display == Configuration.ORIENTATION_PORTRAIT
|
||||
? options.getInt(OPTION_APPWIDGET_MIN_WIDTH, DEFAULT_WIDTH) : options.getInt(
|
||||
OPTION_APPWIDGET_MAX_WIDTH, DEFAULT_WIDTH);
|
||||
int height = display == Configuration.ORIENTATION_PORTRAIT ? options.getInt(
|
||||
OPTION_APPWIDGET_MAX_HEIGHT, DEFAULT_HEIGHT)
|
||||
: options.getInt(OPTION_APPWIDGET_MIN_HEIGHT, DEFAULT_HEIGHT);
|
||||
avatarHeightSpace = height - (8 + 4 + 18 + 8);
|
||||
avatarWidthSpace = width - (4 + 4);
|
||||
}
|
||||
if (DEBUG) Log.d(TAG, "Height: " + avatarHeightSpace + " width: " + avatarWidthSpace);
|
||||
return Math.min(avatarHeightSpace, avatarWidthSpace);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ConversationStatus getBirthdayStatus(PeopleSpaceTile tile,
|
||||
List<ConversationStatus> statuses) {
|
||||
Optional<ConversationStatus> birthdayStatus = statuses.stream().filter(
|
||||
c -> c.getActivity() == ACTIVITY_BIRTHDAY).findFirst();
|
||||
if (birthdayStatus.isPresent()) {
|
||||
return birthdayStatus.get();
|
||||
}
|
||||
if (!TextUtils.isEmpty(tile.getBirthdayText())) {
|
||||
return new ConversationStatus.Builder(tile.getId(), ACTIVITY_BIRTHDAY).build();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a {@code status} should have its own entire templated view.
|
||||
*
|
||||
* <p>A status may still be shown on the view (for example, as a new story ring) even if it's
|
||||
* not valid to compose an entire view.
|
||||
*/
|
||||
private static boolean isStatusValidForEntireStatusView(ConversationStatus status) {
|
||||
switch (status.getActivity()) {
|
||||
// Birthday & Anniversary don't require text provided or icon provided.
|
||||
case ACTIVITY_BIRTHDAY:
|
||||
case ACTIVITY_ANNIVERSARY:
|
||||
return true;
|
||||
default:
|
||||
// For future birthday, location, new story, video, music, game, and other, the
|
||||
// app must provide either text or an icon.
|
||||
return !TextUtils.isEmpty(status.getDescription())
|
||||
|| status.getIcon() != null;
|
||||
}
|
||||
}
|
||||
|
||||
private static RemoteViews createStatusRemoteViews(Context context, ConversationStatus status,
|
||||
int layoutSize) {
|
||||
int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
|
||||
: R.layout.people_space_small_avatar_tile;
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), layout);
|
||||
CharSequence statusText = status.getDescription();
|
||||
if (TextUtils.isEmpty(statusText)) {
|
||||
statusText = getStatusTextByType(context, status.getActivity());
|
||||
}
|
||||
views.setViewVisibility(R.id.subtext, View.GONE);
|
||||
views.setViewVisibility(R.id.text_content, View.VISIBLE);
|
||||
TypedValue typedValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
|
||||
int secondaryTextColor = context.getColor(typedValue.resourceId);
|
||||
views.setInt(R.id.text_content, "setTextColor", secondaryTextColor);
|
||||
views.setTextViewText(R.id.text_content, statusText);
|
||||
Icon statusIcon = status.getIcon();
|
||||
if (statusIcon != null) {
|
||||
views.setImageViewIcon(R.id.image, statusIcon);
|
||||
views.setBoolean(R.id.content_background, "setClipToOutline", true);
|
||||
} else {
|
||||
views.setViewVisibility(R.id.content_background, View.GONE);
|
||||
}
|
||||
// TODO: Set status pre-defined icons
|
||||
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person);
|
||||
ensurePredefinedIconVisibleOnSmallView(views, layoutSize);
|
||||
return views;
|
||||
}
|
||||
|
||||
private static String getStatusTextByType(Context context, int activity) {
|
||||
switch (activity) {
|
||||
case ACTIVITY_BIRTHDAY:
|
||||
return context.getString(R.string.birthday_status);
|
||||
case ACTIVITY_UPCOMING_BIRTHDAY:
|
||||
return context.getString(R.string.upcoming_birthday_status);
|
||||
case ACTIVITY_ANNIVERSARY:
|
||||
return context.getString(R.string.anniversary_status);
|
||||
case ACTIVITY_LOCATION:
|
||||
return context.getString(R.string.location_status);
|
||||
case ACTIVITY_NEW_STORY:
|
||||
return context.getString(R.string.new_story_status);
|
||||
case ACTIVITY_VIDEO:
|
||||
return context.getString(R.string.video_status);
|
||||
case ACTIVITY_AUDIO:
|
||||
return context.getString(R.string.audio_status);
|
||||
case ACTIVITY_GAME:
|
||||
return context.getString(R.string.game_status);
|
||||
default:
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
private static RemoteViews setCommonRemoteViewsFields(Context context, RemoteViews views,
|
||||
PeopleSpaceTile tile, int maxAvatarSize) {
|
||||
try {
|
||||
boolean isAvailable =
|
||||
tile.getStatuses() != null && tile.getStatuses().stream().anyMatch(
|
||||
c -> c.getAvailability() == AVAILABILITY_AVAILABLE);
|
||||
if (isAvailable) {
|
||||
views.setViewVisibility(R.id.availability, View.VISIBLE);
|
||||
} else {
|
||||
views.setViewVisibility(R.id.availability, View.GONE);
|
||||
}
|
||||
boolean hasNewStory =
|
||||
tile.getStatuses() != null && tile.getStatuses().stream().anyMatch(
|
||||
c -> c.getActivity() == ACTIVITY_NEW_STORY);
|
||||
views.setTextViewText(R.id.name, tile.getUserName().toString());
|
||||
views.setBoolean(R.id.content_background, "setClipToOutline", true);
|
||||
Icon icon = tile.getUserIcon();
|
||||
PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(context,
|
||||
context.getPackageManager(),
|
||||
IconDrawableFactory.newInstance(context, false),
|
||||
maxAvatarSize);
|
||||
Drawable drawable = icon.loadDrawable(context);
|
||||
Drawable personDrawable = storyIcon.getPeopleTileDrawable(drawable,
|
||||
tile.getPackageName(), getUserId(tile), tile.isImportantConversation(),
|
||||
hasNewStory);
|
||||
Bitmap bitmap = convertDrawableToBitmap(personDrawable);
|
||||
views.setImageViewBitmap(R.id.person_icon, bitmap);
|
||||
|
||||
return views;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to set common fields: " + e);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
private static RemoteViews setLaunchIntents(Context context, RemoteViews views,
|
||||
PeopleSpaceTile tile, int appWidgetId) {
|
||||
try {
|
||||
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_USER_HANDLE,
|
||||
tile.getUserHandle());
|
||||
activityIntent.putExtra(
|
||||
PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, tile.getNotificationKey());
|
||||
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 add launch intents: " + e);
|
||||
}
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
private static RemoteViews createMissedCallRemoteViews(Context context,
|
||||
PeopleSpaceTile tile, int layoutSize) {
|
||||
int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
|
||||
: R.layout.people_space_small_avatar_tile;
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), layout);
|
||||
views.setViewVisibility(R.id.subtext, View.GONE);
|
||||
views.setViewVisibility(R.id.text_content, View.VISIBLE);
|
||||
views.setTextViewText(R.id.text_content, tile.getNotificationContent());
|
||||
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_phone_missed);
|
||||
ensurePredefinedIconVisibleOnSmallView(views, layoutSize);
|
||||
views.setBoolean(R.id.content_background, "setClipToOutline", true);
|
||||
return views;
|
||||
}
|
||||
|
||||
private static void ensurePredefinedIconVisibleOnSmallView(RemoteViews views, int layoutSize) {
|
||||
if (layoutSize == SMALL_LAYOUT) {
|
||||
views.setViewVisibility(R.id.name, View.GONE);
|
||||
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private static RemoteViews createNotificationRemoteViews(Context context,
|
||||
PeopleSpaceTile tile, int layoutSize) {
|
||||
int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
|
||||
: R.layout.people_space_small_avatar_tile;
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), layout);
|
||||
if (layoutSize != MEDIUM_LAYOUT) {
|
||||
views.setViewVisibility(R.id.name, View.GONE);
|
||||
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
|
||||
}
|
||||
Uri image = tile.getNotificationDataUri();
|
||||
ensurePredefinedIconVisibleOnSmallView(views, layoutSize);
|
||||
if (image != null) {
|
||||
// TODO: Use NotificationInlineImageCache
|
||||
views.setImageViewUri(R.id.image, image);
|
||||
views.setViewVisibility(R.id.content_background, View.VISIBLE);
|
||||
views.setBoolean(R.id.content_background, "setClipToOutline", true);
|
||||
views.setViewVisibility(R.id.text_content, View.GONE);
|
||||
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_photo_camera);
|
||||
} else {
|
||||
CharSequence content = tile.getNotificationContent();
|
||||
views = setPunctuationRemoteViewsFields(views, content);
|
||||
views.setTextViewText(R.id.text_content, tile.getNotificationContent());
|
||||
// TODO: Measure max lines from height.
|
||||
views.setInt(R.id.text_content, "setMaxLines", 2);
|
||||
TypedValue typedValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.textColorPrimary, typedValue, true);
|
||||
int primaryTextColor = context.getColor(typedValue.resourceId);
|
||||
views.setInt(R.id.text_content, "setTextColor", primaryTextColor);
|
||||
views.setViewVisibility(R.id.text_content, View.VISIBLE);
|
||||
views.setViewVisibility(R.id.content_background, View.GONE);
|
||||
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_message);
|
||||
}
|
||||
// TODO: Set subtext as Group Sender name once storing the name in PeopleSpaceTile.
|
||||
views.setViewVisibility(R.id.subtext, View.GONE);
|
||||
return views;
|
||||
}
|
||||
|
||||
private static RemoteViews createLastInteractionRemoteViews(Context context,
|
||||
PeopleSpaceTile tile, int layoutSize) {
|
||||
int layout = layoutSize == SMALL_LAYOUT ? R.layout.people_space_small_view
|
||||
: R.layout.people_space_large_avatar_tile;
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), layout);
|
||||
if (layoutSize == SMALL_LAYOUT) {
|
||||
views.setViewVisibility(R.id.name, View.VISIBLE);
|
||||
views.setViewVisibility(R.id.predefined_icon, View.GONE);
|
||||
}
|
||||
String status = PeopleSpaceUtils.getLastInteractionString(
|
||||
context, tile.getLastInteractionTimestamp());
|
||||
views.setTextViewText(R.id.last_interaction, status);
|
||||
return views;
|
||||
}
|
||||
|
||||
private static RemoteViews setPunctuationRemoteViewsFields(
|
||||
RemoteViews views, CharSequence content) {
|
||||
String punctuation = getBackgroundTextFromMessage(content.toString());
|
||||
int visibility = View.GONE;
|
||||
if (punctuation != null) {
|
||||
visibility = View.VISIBLE;
|
||||
}
|
||||
views.setTextViewText(R.id.punctuation1, punctuation);
|
||||
views.setTextViewText(R.id.punctuation2, punctuation);
|
||||
views.setTextViewText(R.id.punctuation3, punctuation);
|
||||
views.setTextViewText(R.id.punctuation4, punctuation);
|
||||
views.setTextViewText(R.id.punctuation5, punctuation);
|
||||
views.setTextViewText(R.id.punctuation6, punctuation);
|
||||
|
||||
views.setViewVisibility(R.id.punctuation1, visibility);
|
||||
views.setViewVisibility(R.id.punctuation2, visibility);
|
||||
views.setViewVisibility(R.id.punctuation3, visibility);
|
||||
views.setViewVisibility(R.id.punctuation4, visibility);
|
||||
views.setViewVisibility(R.id.punctuation5, visibility);
|
||||
views.setViewVisibility(R.id.punctuation6, visibility);
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
/** Gets character for tile background decoration based on notification content. */
|
||||
@VisibleForTesting
|
||||
static String getBackgroundTextFromMessage(String message) {
|
||||
if (!ANY_DOUBLE_MARK_PATTERN.matcher(message).find()) {
|
||||
return null;
|
||||
}
|
||||
if (MIXED_MARK_PATTERN.matcher(message).find()) {
|
||||
return "!?";
|
||||
}
|
||||
Matcher doubleQuestionMatcher = DOUBLE_QUESTION_PATTERN.matcher(message);
|
||||
if (!doubleQuestionMatcher.find()) {
|
||||
return "!";
|
||||
}
|
||||
Matcher doubleExclamationMatcher = DOUBLE_EXCLAMATION_PATTERN.matcher(message);
|
||||
if (!doubleExclamationMatcher.find()) {
|
||||
return "?";
|
||||
}
|
||||
// If we have both "!!" and "??", return the one that comes first.
|
||||
if (doubleQuestionMatcher.start() < doubleExclamationMatcher.start()) {
|
||||
return "?";
|
||||
}
|
||||
return "!";
|
||||
}
|
||||
|
||||
/** Gets the most recent {@link Notification.MessagingStyle.Message} from the notification. */
|
||||
@VisibleForTesting
|
||||
public static Notification.MessagingStyle.Message getLastMessagingStyleMessage(
|
||||
@@ -917,7 +521,8 @@ public class PeopleSpaceUtils {
|
||||
public static void updateAppWidgetViews(AppWidgetManager appWidgetManager,
|
||||
Context context, int appWidgetId, PeopleSpaceTile tile, Bundle options) {
|
||||
if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
|
||||
RemoteViews views = createRemoteViews(context, tile, appWidgetId, options);
|
||||
RemoteViews views = new PeopleTileViewHelper(context, tile, appWidgetId,
|
||||
options).getViews();
|
||||
|
||||
// Tell the AppWidgetManager to perform an update on the current app widget.
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
@@ -1005,7 +610,7 @@ public class PeopleSpaceUtils {
|
||||
|
||||
if (DEBUG) Log.i(TAG, "Returning tile preview for shortcutId: " + shortcutId);
|
||||
Bundle bundle = new Bundle();
|
||||
return PeopleSpaceUtils.createRemoteViews(context, augmentedTile, 0, bundle);
|
||||
return new PeopleTileViewHelper(context, augmentedTile, 0, bundle).getViews();
|
||||
}
|
||||
|
||||
/** Returns the userId associated with a {@link PeopleSpaceTile} */
|
||||
|
||||
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.systemui.people;
|
||||
|
||||
import static android.app.Notification.CATEGORY_MISSED_CALL;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_AUDIO;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_LOCATION;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_UPCOMING_BIRTHDAY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_VIDEO;
|
||||
import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE;
|
||||
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
|
||||
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH;
|
||||
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT;
|
||||
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
|
||||
|
||||
import static com.android.systemui.people.PeopleSpaceUtils.convertDrawableToBitmap;
|
||||
import static com.android.systemui.people.PeopleSpaceUtils.getUserId;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.people.ConversationStatus;
|
||||
import android.app.people.PeopleSpaceTile;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.IconDrawableFactory;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.people.widget.LaunchConversationActivity;
|
||||
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class PeopleTileViewHelper {
|
||||
/** Turns on debugging information about People Space. */
|
||||
public static final boolean DEBUG = true;
|
||||
private static final String TAG = "PeopleTileView";
|
||||
|
||||
public static final int LAYOUT_SMALL = 0;
|
||||
public static final int LAYOUT_MEDIUM = 1;
|
||||
public static final int LAYOUT_LARGE = 2;
|
||||
|
||||
private static final int MIN_CONTENT_MAX_LINES = 2;
|
||||
|
||||
private static final int FIXED_HEIGHT_DIMENS_FOR_LARGE_CONTENT = 14 + 12 + 4 + 16;
|
||||
private static final int FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT = 8 + 4 + 4 + 8;
|
||||
private static final int FIXED_HEIGHT_DIMENS_FOR_SMALL = 6 + 4 + 8;
|
||||
private static final int FIXED_WIDTH_DIMENS_FOR_SMALL = 4 + 4;
|
||||
|
||||
private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
|
||||
private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
|
||||
private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
|
||||
private static final Pattern MIXED_MARK_PATTERN = Pattern.compile("![?].*|.*[?]!");
|
||||
|
||||
public static final String EMPTY_STRING = "";
|
||||
|
||||
private Context mContext;
|
||||
private PeopleSpaceTile mTile;
|
||||
private float mDensity;
|
||||
private int mAppWidgetId;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
private int mLayoutSize;
|
||||
|
||||
PeopleTileViewHelper(Context context, PeopleSpaceTile tile,
|
||||
int appWidgetId, Bundle options) {
|
||||
mContext = context;
|
||||
mTile = tile;
|
||||
mAppWidgetId = appWidgetId;
|
||||
mDensity = mContext.getResources().getDisplayMetrics().density;
|
||||
int display = mContext.getResources().getConfiguration().orientation;
|
||||
mWidth = display == Configuration.ORIENTATION_PORTRAIT
|
||||
? options.getInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.default_width)) : options.getInt(
|
||||
OPTION_APPWIDGET_MAX_WIDTH,
|
||||
getSizeInDp(R.dimen.default_width));
|
||||
mHeight = display == Configuration.ORIENTATION_PORTRAIT ? options.getInt(
|
||||
OPTION_APPWIDGET_MAX_HEIGHT,
|
||||
getSizeInDp(R.dimen.default_height))
|
||||
: options.getInt(OPTION_APPWIDGET_MIN_HEIGHT,
|
||||
getSizeInDp(R.dimen.default_height));
|
||||
mLayoutSize = getLayoutSize();
|
||||
}
|
||||
|
||||
public RemoteViews getViews() {
|
||||
RemoteViews viewsForTile = getViewForTile();
|
||||
int maxAvatarSize = getMaxAvatarSize(viewsForTile);
|
||||
RemoteViews views = setCommonRemoteViewsFields(viewsForTile, maxAvatarSize);
|
||||
return setLaunchIntents(views);
|
||||
}
|
||||
|
||||
/**
|
||||
* The prioritization for the {@code mTile} content is missed calls, followed by notification
|
||||
* content, then birthdays, then the most recent status, and finally last interaction.
|
||||
*/
|
||||
private RemoteViews getViewForTile() {
|
||||
if (Objects.equals(mTile.getNotificationCategory(), CATEGORY_MISSED_CALL)) {
|
||||
if (DEBUG) Log.d(TAG, "Create missed call view");
|
||||
return createMissedCallRemoteViews();
|
||||
}
|
||||
|
||||
if (mTile.getNotificationKey() != null) {
|
||||
if (DEBUG) Log.d(TAG, "Create notification view");
|
||||
return createNotificationRemoteViews();
|
||||
}
|
||||
|
||||
// TODO: Add sorting when we expose timestamp of statuses.
|
||||
List<ConversationStatus> statusesForEntireView =
|
||||
mTile.getStatuses() == null ? Arrays.asList() : mTile.getStatuses().stream().filter(
|
||||
c -> isStatusValidForEntireStatusView(c)).collect(Collectors.toList());
|
||||
ConversationStatus birthdayStatus = getBirthdayStatus(statusesForEntireView);
|
||||
if (birthdayStatus != null) {
|
||||
if (DEBUG) Log.d(TAG, "Create birthday view");
|
||||
return createStatusRemoteViews(birthdayStatus);
|
||||
}
|
||||
|
||||
if (!statusesForEntireView.isEmpty()) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG,
|
||||
"Create status view for: " + statusesForEntireView.get(0).getActivity());
|
||||
}
|
||||
return createStatusRemoteViews(statusesForEntireView.get(0));
|
||||
}
|
||||
|
||||
return createLastInteractionRemoteViews();
|
||||
}
|
||||
|
||||
private void setMaxLines(RemoteViews views) {
|
||||
int textSize = mLayoutSize == LAYOUT_LARGE ? getSizeInDp(
|
||||
R.dimen.content_text_size_for_medium)
|
||||
: getSizeInDp(R.dimen.content_text_size_for_medium);
|
||||
int lineHeight = getLineHeight(textSize);
|
||||
int notificationContentHeight = getContentHeightForLayout(lineHeight);
|
||||
int maxAdaptiveLines = Math.floorDiv(notificationContentHeight, lineHeight);
|
||||
int maxLines = Math.max(MIN_CONTENT_MAX_LINES, maxAdaptiveLines);
|
||||
views.setInt(R.id.text_content, "setMaxLines", maxLines);
|
||||
}
|
||||
|
||||
private int getLineHeight(int textSize) {
|
||||
try {
|
||||
TextView text = new TextView(mContext);
|
||||
text.setTextSize(textSize);
|
||||
int lineHeight = (int) (text.getLineHeight() / mDensity);
|
||||
return lineHeight;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Could not create text view: " + e);
|
||||
return getSizeInDp(
|
||||
R.dimen.content_text_size_for_medium);
|
||||
}
|
||||
}
|
||||
|
||||
private int getSizeInDp(int dimenResourceId) {
|
||||
return (int) (mContext.getResources().getDimension(dimenResourceId) / mDensity);
|
||||
}
|
||||
|
||||
private int getContentHeightForLayout(int lineHeight) {
|
||||
switch (mLayoutSize) {
|
||||
case LAYOUT_MEDIUM:
|
||||
return mHeight - (lineHeight + FIXED_HEIGHT_DIMENS_FOR_MEDIUM_CONTENT);
|
||||
case LAYOUT_LARGE:
|
||||
return mHeight - (getSizeInDp(
|
||||
R.dimen.max_people_avatar_size_for_large_content) + lineHeight
|
||||
+ FIXED_HEIGHT_DIMENS_FOR_LARGE_CONTENT);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Calculates the best layout relative to the size in {@code options}. */
|
||||
private int getLayoutSize() {
|
||||
if (mHeight >= getSizeInDp(R.dimen.required_width_for_large)
|
||||
&& mWidth >= getSizeInDp(R.dimen.required_width_for_large)) {
|
||||
if (DEBUG) Log.d(TAG, "Large view for mWidth: " + mWidth + " mHeight: " + mHeight);
|
||||
return LAYOUT_LARGE;
|
||||
}
|
||||
// Small layout used below a certain minimum mWidth with any mHeight.
|
||||
if (mWidth >= getSizeInDp(R.dimen.required_width_for_medium)) {
|
||||
if (DEBUG) Log.d(TAG, "Medium view for mWidth: " + mWidth + " mHeight: " + mHeight);
|
||||
return LAYOUT_MEDIUM;
|
||||
}
|
||||
// Small layout can always handle our minimum mWidth and mHeight for our widget.
|
||||
if (DEBUG) Log.d(TAG, "Small view for mWidth: " + mWidth + " mHeight: " + mHeight);
|
||||
return LAYOUT_SMALL;
|
||||
}
|
||||
|
||||
/** Returns the max avatar size for {@code views} under the current {@code options}. */
|
||||
private int getMaxAvatarSize(RemoteViews views) {
|
||||
int layoutId = views.getLayoutId();
|
||||
int avatarSize = getSizeInDp(R.dimen.avatar_size_for_medium);
|
||||
if (layoutId == R.layout.people_tile_medium_empty) {
|
||||
return getSizeInDp(
|
||||
R.dimen.max_people_avatar_size_for_large_content);
|
||||
}
|
||||
if (layoutId == R.layout.people_tile_medium_with_content) {
|
||||
return getSizeInDp(R.dimen.avatar_size_for_medium);
|
||||
}
|
||||
|
||||
// Calculate adaptive avatar size for remaining layouts.
|
||||
if (layoutId == R.layout.people_tile_small) {
|
||||
int avatarHeightSpace = mHeight - (FIXED_HEIGHT_DIMENS_FOR_SMALL + Math.max(18,
|
||||
getLineHeight(getSizeInDp(
|
||||
R.dimen.name_text_size_for_small))));
|
||||
int avatarWidthSpace = mWidth - FIXED_WIDTH_DIMENS_FOR_SMALL;
|
||||
avatarSize = Math.min(avatarHeightSpace, avatarWidthSpace);
|
||||
}
|
||||
|
||||
if (layoutId == R.layout.people_tile_large_with_content) {
|
||||
avatarSize = mHeight - (FIXED_HEIGHT_DIMENS_FOR_LARGE_CONTENT + (getLineHeight(
|
||||
getSizeInDp(R.dimen.content_text_size_for_large))
|
||||
* 3));
|
||||
return Math.min(avatarSize, getSizeInDp(
|
||||
R.dimen.max_people_avatar_size_for_large_content));
|
||||
}
|
||||
|
||||
if (layoutId == R.layout.people_tile_large_empty) {
|
||||
int avatarHeightSpace = mHeight - (14 + 14 + getLineHeight(
|
||||
getSizeInDp(R.dimen.name_text_size_for_large))
|
||||
+ getLineHeight(
|
||||
getSizeInDp(R.dimen.content_text_size_for_large))
|
||||
+ 16 + 10 + 14);
|
||||
int avatarWidthSpace = mWidth - (14 + 14);
|
||||
avatarSize = Math.min(avatarHeightSpace, avatarWidthSpace);
|
||||
}
|
||||
return Math.min(avatarSize,
|
||||
getSizeInDp(R.dimen.max_people_avatar_size));
|
||||
}
|
||||
|
||||
private RemoteViews setCommonRemoteViewsFields(RemoteViews views,
|
||||
int maxAvatarSize) {
|
||||
try {
|
||||
boolean isAvailable =
|
||||
mTile.getStatuses() != null && mTile.getStatuses().stream().anyMatch(
|
||||
c -> c.getAvailability() == AVAILABILITY_AVAILABLE);
|
||||
if (isAvailable) {
|
||||
views.setViewVisibility(R.id.availability, View.VISIBLE);
|
||||
} else {
|
||||
views.setViewVisibility(R.id.availability, View.GONE);
|
||||
}
|
||||
boolean hasNewStory =
|
||||
mTile.getStatuses() != null && mTile.getStatuses().stream().anyMatch(
|
||||
c -> c.getActivity() == ACTIVITY_NEW_STORY);
|
||||
views.setTextViewText(R.id.name, mTile.getUserName().toString());
|
||||
views.setBoolean(R.id.image, "setClipToOutline", true);
|
||||
|
||||
Icon icon = mTile.getUserIcon();
|
||||
PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(mContext,
|
||||
mContext.getPackageManager(),
|
||||
IconDrawableFactory.newInstance(mContext, false),
|
||||
maxAvatarSize);
|
||||
Drawable drawable = icon.loadDrawable(mContext);
|
||||
Drawable personDrawable = storyIcon.getPeopleTileDrawable(drawable,
|
||||
mTile.getPackageName(), getUserId(mTile), mTile.isImportantConversation(),
|
||||
hasNewStory);
|
||||
Bitmap bitmap = convertDrawableToBitmap(personDrawable);
|
||||
views.setImageViewBitmap(R.id.person_icon, bitmap);
|
||||
|
||||
return views;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to set common fields: " + e);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
private RemoteViews setLaunchIntents(RemoteViews views) {
|
||||
try {
|
||||
Intent activityIntent = new Intent(mContext, 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, mTile.getId());
|
||||
activityIntent.putExtra(
|
||||
PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, mTile.getPackageName());
|
||||
activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE,
|
||||
mTile.getUserHandle());
|
||||
activityIntent.putExtra(
|
||||
PeopleSpaceWidgetProvider.EXTRA_NOTIFICATION_KEY, mTile.getNotificationKey());
|
||||
views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity(
|
||||
mContext,
|
||||
mAppWidgetId,
|
||||
activityIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE));
|
||||
return views;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to add launch intents: " + e);
|
||||
}
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
private RemoteViews createMissedCallRemoteViews() {
|
||||
RemoteViews views = getViewForContentLayout();
|
||||
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
|
||||
setMaxLines(views);
|
||||
views.setTextViewText(R.id.text_content, mTile.getNotificationContent());
|
||||
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_phone_missed);
|
||||
return views;
|
||||
}
|
||||
|
||||
private RemoteViews createNotificationRemoteViews() {
|
||||
RemoteViews views = getViewForContentLayout();
|
||||
Uri image = mTile.getNotificationDataUri();
|
||||
if (image != null) {
|
||||
// TODO: Use NotificationInlineImageCache
|
||||
views.setImageViewUri(R.id.image, image);
|
||||
views.setViewVisibility(R.id.image, View.VISIBLE);
|
||||
views.setViewVisibility(R.id.text_content, View.GONE);
|
||||
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_photo_camera);
|
||||
} else {
|
||||
setMaxLines(views);
|
||||
CharSequence content = mTile.getNotificationContent();
|
||||
views = setPunctuationRemoteViewsFields(views, content);
|
||||
TypedValue typedValue = new TypedValue();
|
||||
mContext.getTheme().resolveAttribute(android.R.attr.textColorPrimary, typedValue, true);
|
||||
int primaryTextColor = mContext.getColor(typedValue.resourceId);
|
||||
views.setInt(R.id.text_content, "setTextColor", primaryTextColor);
|
||||
views.setTextViewText(R.id.text_content, mTile.getNotificationContent());
|
||||
views.setViewVisibility(R.id.image, View.GONE);
|
||||
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_message);
|
||||
}
|
||||
// TODO: Set subtext as Group Sender name once storing the name in PeopleSpaceTile and
|
||||
// subtract 1 from maxLines when present.
|
||||
views.setViewVisibility(R.id.subtext, View.GONE);
|
||||
return views;
|
||||
}
|
||||
|
||||
private RemoteViews createStatusRemoteViews(ConversationStatus status) {
|
||||
RemoteViews views = getViewForContentLayout();
|
||||
CharSequence statusText = status.getDescription();
|
||||
if (TextUtils.isEmpty(statusText)) {
|
||||
statusText = getStatusTextByType(status.getActivity());
|
||||
}
|
||||
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
|
||||
setMaxLines(views);
|
||||
// Secondary text color for statuses.
|
||||
TypedValue typedValue = new TypedValue();
|
||||
mContext.getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
|
||||
int secondaryTextColor = mContext.getColor(typedValue.resourceId);
|
||||
views.setInt(R.id.text_content, "setTextColor", secondaryTextColor);
|
||||
views.setTextViewText(R.id.text_content, statusText);
|
||||
|
||||
Icon statusIcon = status.getIcon();
|
||||
if (statusIcon != null) {
|
||||
// No multi-line text with status images on medium layout.
|
||||
views.setViewVisibility(R.id.text_content, View.GONE);
|
||||
// Show 1-line subtext on large layout with status images.
|
||||
if (mLayoutSize == LAYOUT_LARGE) {
|
||||
views.setViewVisibility(R.id.subtext, View.VISIBLE);
|
||||
views.setTextViewText(R.id.subtext, statusText);
|
||||
}
|
||||
views.setViewVisibility(R.id.image, View.VISIBLE);
|
||||
views.setImageViewIcon(R.id.image, statusIcon);
|
||||
} else {
|
||||
views.setViewVisibility(R.id.image, View.GONE);
|
||||
}
|
||||
// TODO: Set status pre-defined icons
|
||||
views.setImageViewResource(R.id.predefined_icon, R.drawable.ic_person);
|
||||
return views;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ConversationStatus getBirthdayStatus(
|
||||
List<ConversationStatus> statuses) {
|
||||
Optional<ConversationStatus> birthdayStatus = statuses.stream().filter(
|
||||
c -> c.getActivity() == ACTIVITY_BIRTHDAY).findFirst();
|
||||
if (birthdayStatus.isPresent()) {
|
||||
return birthdayStatus.get();
|
||||
}
|
||||
if (!TextUtils.isEmpty(mTile.getBirthdayText())) {
|
||||
return new ConversationStatus.Builder(mTile.getId(), ACTIVITY_BIRTHDAY).build();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a {@code status} should have its own entire templated view.
|
||||
*
|
||||
* <p>A status may still be shown on the view (for example, as a new story ring) even if it's
|
||||
* not valid to compose an entire view.
|
||||
*/
|
||||
private boolean isStatusValidForEntireStatusView(ConversationStatus status) {
|
||||
switch (status.getActivity()) {
|
||||
// Birthday & Anniversary don't require text provided or icon provided.
|
||||
case ACTIVITY_BIRTHDAY:
|
||||
case ACTIVITY_ANNIVERSARY:
|
||||
return true;
|
||||
default:
|
||||
// For future birthday, location, new story, video, music, game, and other, the
|
||||
// app must provide either text or an icon.
|
||||
return !TextUtils.isEmpty(status.getDescription())
|
||||
|| status.getIcon() != null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getStatusTextByType(int activity) {
|
||||
switch (activity) {
|
||||
case ACTIVITY_BIRTHDAY:
|
||||
return mContext.getString(R.string.birthday_status);
|
||||
case ACTIVITY_UPCOMING_BIRTHDAY:
|
||||
return mContext.getString(R.string.upcoming_birthday_status);
|
||||
case ACTIVITY_ANNIVERSARY:
|
||||
return mContext.getString(R.string.anniversary_status);
|
||||
case ACTIVITY_LOCATION:
|
||||
return mContext.getString(R.string.location_status);
|
||||
case ACTIVITY_NEW_STORY:
|
||||
return mContext.getString(R.string.new_story_status);
|
||||
case ACTIVITY_VIDEO:
|
||||
return mContext.getString(R.string.video_status);
|
||||
case ACTIVITY_AUDIO:
|
||||
return mContext.getString(R.string.audio_status);
|
||||
case ACTIVITY_GAME:
|
||||
return mContext.getString(R.string.game_status);
|
||||
default:
|
||||
return EMPTY_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteViews setPunctuationRemoteViewsFields(
|
||||
RemoteViews views, CharSequence content) {
|
||||
String punctuation = getBackgroundTextFromMessage(content.toString());
|
||||
int visibility = View.GONE;
|
||||
if (punctuation != null) {
|
||||
visibility = View.VISIBLE;
|
||||
}
|
||||
views.setTextViewText(R.id.punctuation1, punctuation);
|
||||
views.setTextViewText(R.id.punctuation2, punctuation);
|
||||
views.setTextViewText(R.id.punctuation3, punctuation);
|
||||
views.setTextViewText(R.id.punctuation4, punctuation);
|
||||
views.setTextViewText(R.id.punctuation5, punctuation);
|
||||
views.setTextViewText(R.id.punctuation6, punctuation);
|
||||
|
||||
views.setViewVisibility(R.id.punctuation1, visibility);
|
||||
views.setViewVisibility(R.id.punctuation2, visibility);
|
||||
views.setViewVisibility(R.id.punctuation3, visibility);
|
||||
views.setViewVisibility(R.id.punctuation4, visibility);
|
||||
views.setViewVisibility(R.id.punctuation5, visibility);
|
||||
views.setViewVisibility(R.id.punctuation6, visibility);
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
/** Gets character for mTile background decoration based on notification content. */
|
||||
@VisibleForTesting
|
||||
String getBackgroundTextFromMessage(String message) {
|
||||
if (!ANY_DOUBLE_MARK_PATTERN.matcher(message).find()) {
|
||||
return null;
|
||||
}
|
||||
if (MIXED_MARK_PATTERN.matcher(message).find()) {
|
||||
return "!?";
|
||||
}
|
||||
Matcher doubleQuestionMatcher = DOUBLE_QUESTION_PATTERN.matcher(message);
|
||||
if (!doubleQuestionMatcher.find()) {
|
||||
return "!";
|
||||
}
|
||||
Matcher doubleExclamationMatcher = DOUBLE_EXCLAMATION_PATTERN.matcher(message);
|
||||
if (!doubleExclamationMatcher.find()) {
|
||||
return "?";
|
||||
}
|
||||
// If we have both "!!" and "??", return the one that comes first.
|
||||
if (doubleQuestionMatcher.start() < doubleExclamationMatcher.start()) {
|
||||
return "?";
|
||||
}
|
||||
return "!";
|
||||
}
|
||||
|
||||
private RemoteViews getViewForContentLayout() {
|
||||
RemoteViews views = new RemoteViews(mContext.getPackageName(),
|
||||
getLayoutForContent());
|
||||
if (mLayoutSize == LAYOUT_SMALL) {
|
||||
views.setViewVisibility(R.id.predefined_icon, View.VISIBLE);
|
||||
views.setViewVisibility(R.id.name, View.GONE);
|
||||
} else {
|
||||
views.setViewVisibility(R.id.predefined_icon, View.GONE);
|
||||
views.setViewVisibility(R.id.name, View.VISIBLE);
|
||||
views.setViewVisibility(R.id.text_content, View.VISIBLE);
|
||||
views.setViewVisibility(R.id.subtext, View.GONE);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
|
||||
private RemoteViews createLastInteractionRemoteViews() {
|
||||
RemoteViews views = new RemoteViews(mContext.getPackageName(), getEmptyLayout());
|
||||
if (mLayoutSize == LAYOUT_SMALL) {
|
||||
views.setViewVisibility(R.id.name, View.VISIBLE);
|
||||
views.setViewVisibility(R.id.predefined_icon, View.GONE);
|
||||
}
|
||||
String status = PeopleSpaceUtils.getLastInteractionString(mContext,
|
||||
mTile.getLastInteractionTimestamp());
|
||||
views.setTextViewText(R.id.last_interaction, status);
|
||||
return views;
|
||||
}
|
||||
|
||||
private int getEmptyLayout() {
|
||||
switch (mLayoutSize) {
|
||||
case LAYOUT_MEDIUM:
|
||||
return R.layout.people_tile_medium_empty;
|
||||
case LAYOUT_LARGE:
|
||||
return R.layout.people_tile_large_empty;
|
||||
case LAYOUT_SMALL:
|
||||
default:
|
||||
return R.layout.people_tile_small;
|
||||
}
|
||||
}
|
||||
|
||||
private int getLayoutForContent() {
|
||||
switch (mLayoutSize) {
|
||||
case LAYOUT_MEDIUM:
|
||||
return R.layout.people_tile_medium_with_content;
|
||||
case LAYOUT_LARGE:
|
||||
return R.layout.people_tile_large_with_content;
|
||||
case LAYOUT_SMALL:
|
||||
default:
|
||||
return R.layout.people_tile_small;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,20 +16,11 @@
|
||||
|
||||
package com.android.systemui.people;
|
||||
|
||||
import static android.app.Notification.CATEGORY_MISSED_CALL;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
|
||||
import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE;
|
||||
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
|
||||
|
||||
import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
|
||||
import static com.android.systemui.people.PeopleSpaceUtils.REQUIRED_WIDTH_FOR_MEDIUM;
|
||||
import static com.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
@@ -48,7 +39,6 @@ import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Person;
|
||||
import android.app.people.ConversationChannel;
|
||||
import android.app.people.ConversationStatus;
|
||||
import android.app.people.IPeopleManager;
|
||||
import android.app.people.PeopleSpaceTile;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
@@ -70,9 +60,7 @@ import android.provider.ContactsContract;
|
||||
import android.service.notification.ConversationChannelWrapper;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.TextView;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -117,8 +105,6 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
|
||||
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 String GAME_DESCRIPTION = "Playing a game!";
|
||||
private static final CharSequence MISSED_CALL = "Custom missed call message";
|
||||
private static final String NAME = "username";
|
||||
private static final Person PERSON = new Person.Builder()
|
||||
.setName("name")
|
||||
@@ -126,11 +112,6 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
|
||||
.setUri(URI.toString())
|
||||
.setBot(false)
|
||||
.build();
|
||||
private static final PeopleSpaceTile PERSON_TILE_WITHOUT_NOTIFICATION =
|
||||
new PeopleSpaceTile
|
||||
.Builder(SHORTCUT_ID_1, NAME, ICON, new Intent())
|
||||
.setLastInteractionTimestamp(0L)
|
||||
.build();
|
||||
private static final PeopleSpaceTile PERSON_TILE =
|
||||
new PeopleSpaceTile
|
||||
.Builder(SHORTCUT_ID_1, NAME, ICON, new Intent())
|
||||
@@ -139,16 +120,6 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
|
||||
.setNotificationContent(NOTIFICATION_CONTENT)
|
||||
.setNotificationDataUri(URI)
|
||||
.build();
|
||||
private static final ConversationStatus GAME_STATUS =
|
||||
new ConversationStatus
|
||||
.Builder(PERSON_TILE.getId(), ACTIVITY_GAME)
|
||||
.setDescription(GAME_DESCRIPTION)
|
||||
.build();
|
||||
private static final ConversationStatus NEW_STORY_WITH_AVAILABILITY =
|
||||
new ConversationStatus
|
||||
.Builder(PERSON_TILE.getId(), ACTIVITY_NEW_STORY)
|
||||
.setAvailability(AVAILABILITY_AVAILABLE)
|
||||
.build();
|
||||
|
||||
private final ShortcutInfo mShortcutInfo = new ShortcutInfo.Builder(mContext,
|
||||
SHORTCUT_ID_1).setLongLabel(
|
||||
@@ -244,6 +215,12 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
|
||||
when(mAppWidgetManager.getAppWidgetOptions(eq(WIDGET_ID_WITHOUT_SHORTCUT)))
|
||||
.thenReturn(new Bundle());
|
||||
|
||||
Configuration configuration = mock(Configuration.class);
|
||||
DisplayMetrics displayMetrics = mock(DisplayMetrics.class);
|
||||
Resources resources = mock(Resources.class);
|
||||
when(mMockContext.getResources()).thenReturn(resources);
|
||||
when(resources.getConfiguration()).thenReturn(configuration);
|
||||
when(resources.getDisplayMetrics()).thenReturn(displayMetrics);
|
||||
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
|
||||
when(mMockContentResolver.query(any(Uri.class), any(), anyString(), any(),
|
||||
isNull())).thenReturn(mMockCursor);
|
||||
@@ -254,10 +231,6 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
|
||||
when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mMockContext.getString(R.string.over_timestamp)).thenReturn(
|
||||
mContext.getString(R.string.over_timestamp));
|
||||
Configuration configuration = mock(Configuration.class);
|
||||
Resources resources = mock(Resources.class);
|
||||
when(mMockContext.getResources()).thenReturn(resources);
|
||||
when(resources.getConfiguration()).thenReturn(configuration);
|
||||
when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null);
|
||||
when(mNotificationEntryManager.getVisibleNotifications())
|
||||
.thenReturn(List.of(mNotificationEntry1, mNotificationEntry2, mNotificationEntry3));
|
||||
@@ -361,95 +334,6 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
|
||||
assertThat(lastMessage).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageNoPunctuation() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("test");
|
||||
|
||||
assertThat(backgroundText).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageSingleExclamation() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("test!");
|
||||
|
||||
assertThat(backgroundText).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageSingleQuestion() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("?test");
|
||||
|
||||
assertThat(backgroundText).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageSeparatedMarks() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("test! right!");
|
||||
|
||||
assertThat(backgroundText).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageDoubleExclamation() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("!!test");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageDoubleQuestion() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("test??");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageMixed() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage("test?!");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageMixedInTheMiddle() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage(
|
||||
"test!? in the middle");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageMixedDifferentOrder() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage(
|
||||
"test!? in the middle");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageMultiple() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage(
|
||||
"test!?!!? in the middle");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageQuestionFirst() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage(
|
||||
"test?? in the middle!!");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageExclamationFirst() {
|
||||
String backgroundText = PeopleSpaceUtils.getBackgroundTextFromMessage(
|
||||
"test!! in the middle??");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLastMessagingStyleMessage() {
|
||||
StatusBarNotification sbn = new SbnBuilder()
|
||||
@@ -687,225 +571,6 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
|
||||
any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithLastInteractionTime() {
|
||||
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mMockContext,
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions);
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
// Has last interaction.
|
||||
TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
|
||||
assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
|
||||
// No availability.
|
||||
View availability = result.findViewById(R.id.availability);
|
||||
assertEquals(View.GONE, availability.getVisibility());
|
||||
// Shows person icon.
|
||||
View personIcon = result.findViewById(R.id.person_icon);
|
||||
assertEquals(View.VISIBLE, personIcon.getVisibility());
|
||||
// No status.
|
||||
assertThat((View) result.findViewById(R.id.text_content)).isNull();
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
|
||||
RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions);
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show name over predefined icon.
|
||||
assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Shows person icon.
|
||||
assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithGameTypeOnlyIsIgnored() {
|
||||
PeopleSpaceTile tileWithAvailabilityAndNewStory =
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
|
||||
Arrays.asList(NEW_STORY_WITH_AVAILABILITY,
|
||||
new ConversationStatus.Builder(
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
|
||||
ACTIVITY_GAME).build())).build();
|
||||
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mMockContext,
|
||||
tileWithAvailabilityAndNewStory, 0, mOptions);
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
// Has last interaction over status.
|
||||
TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
|
||||
assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
|
||||
// Has availability.
|
||||
View availability = result.findViewById(R.id.availability);
|
||||
assertEquals(View.VISIBLE, availability.getVisibility());
|
||||
// Has person icon.
|
||||
View personIcon = result.findViewById(R.id.person_icon);
|
||||
assertEquals(View.VISIBLE, personIcon.getVisibility());
|
||||
// No status.
|
||||
assertThat((View) result.findViewById(R.id.text_content)).isNull();
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
|
||||
RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
|
||||
tileWithAvailabilityAndNewStory, 0, mOptions);
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show name rather than game type.
|
||||
assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithBirthdayTypeOnlyIsNotIgnored() {
|
||||
PeopleSpaceTile tileWithStatusTemplate =
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
|
||||
Arrays.asList(
|
||||
NEW_STORY_WITH_AVAILABILITY, new ConversationStatus.Builder(
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
|
||||
ACTIVITY_BIRTHDAY).build())).build();
|
||||
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
|
||||
tileWithStatusTemplate, 0, mOptions);
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
// Has availability.
|
||||
View availability = result.findViewById(R.id.availability);
|
||||
assertEquals(View.VISIBLE, availability.getVisibility());
|
||||
// Has person icon.
|
||||
View personIcon = result.findViewById(R.id.person_icon);
|
||||
assertEquals(View.VISIBLE, personIcon.getVisibility());
|
||||
// Has status text from backup text.
|
||||
TextView statusContent = (TextView) result.findViewById(R.id.text_content);
|
||||
assertEquals(statusContent.getText(), mContext.getString(R.string.birthday_status));
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
|
||||
RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
|
||||
tileWithStatusTemplate, 0, mOptions);
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show icon instead of name.
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithStatusTemplate() {
|
||||
PeopleSpaceTile tileWithStatusTemplate =
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
|
||||
Arrays.asList(GAME_STATUS,
|
||||
NEW_STORY_WITH_AVAILABILITY)).build();
|
||||
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
|
||||
tileWithStatusTemplate, 0, mOptions);
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
// Has availability.
|
||||
View availability = result.findViewById(R.id.availability);
|
||||
assertEquals(View.VISIBLE, availability.getVisibility());
|
||||
// Has person icon.
|
||||
View personIcon = result.findViewById(R.id.person_icon);
|
||||
assertEquals(View.VISIBLE, personIcon.getVisibility());
|
||||
// Has status.
|
||||
TextView statusContent = (TextView) result.findViewById(R.id.text_content);
|
||||
assertEquals(statusContent.getText(), GAME_DESCRIPTION);
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
|
||||
RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
|
||||
tileWithStatusTemplate, 0, mOptions);
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show icon instead of name.
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithMissedCallNotification() {
|
||||
PeopleSpaceTile tileWithMissedCallNotification = PERSON_TILE.toBuilder()
|
||||
.setNotificationDataUri(null)
|
||||
.setNotificationCategory(CATEGORY_MISSED_CALL)
|
||||
.setNotificationContent(MISSED_CALL)
|
||||
.build();
|
||||
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
|
||||
tileWithMissedCallNotification, 0, mOptions);
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
// Has availability.
|
||||
View availability = result.findViewById(R.id.availability);
|
||||
assertEquals(View.GONE, availability.getVisibility());
|
||||
// Has person icon.
|
||||
View personIcon = result.findViewById(R.id.person_icon);
|
||||
assertEquals(View.VISIBLE, personIcon.getVisibility());
|
||||
// Has missed call notification content.
|
||||
TextView statusContent = (TextView) result.findViewById(R.id.text_content);
|
||||
assertEquals(statusContent.getText(), MISSED_CALL);
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
|
||||
RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
|
||||
tileWithMissedCallNotification, 0, mOptions);
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show icon instead of name.
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithNotificationTemplate() {
|
||||
PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE.toBuilder()
|
||||
.setNotificationDataUri(null)
|
||||
.setStatuses(Arrays.asList(GAME_STATUS,
|
||||
NEW_STORY_WITH_AVAILABILITY)).build();
|
||||
RemoteViews views = PeopleSpaceUtils.createRemoteViews(mContext,
|
||||
tileWithStatusAndNotification, 0, mOptions);
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
TextView subtext = (TextView) result.findViewById(R.id.subtext);
|
||||
assertEquals(View.GONE, subtext.getVisibility());
|
||||
// Has availability.
|
||||
View availability = result.findViewById(R.id.availability);
|
||||
assertEquals(View.VISIBLE, availability.getVisibility());
|
||||
// Has person icon.
|
||||
View personIcon = result.findViewById(R.id.person_icon);
|
||||
assertEquals(View.VISIBLE, personIcon.getVisibility());
|
||||
// Has notification content.
|
||||
TextView statusContent = (TextView) result.findViewById(R.id.text_content);
|
||||
assertEquals(statusContent.getText(), NOTIFICATION_CONTENT);
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH, REQUIRED_WIDTH_FOR_MEDIUM - 1);
|
||||
RemoteViews smallView = PeopleSpaceUtils.createRemoteViews(mContext,
|
||||
tileWithStatusAndNotification, 0, mOptions);
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show icon instead of name.
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
}
|
||||
|
||||
private ConversationChannelWrapper getConversationChannelWrapper(String shortcutId,
|
||||
boolean importantConversation, long lastInteractionTimestamp) throws Exception {
|
||||
ConversationChannelWrapper convo = new ConversationChannelWrapper();
|
||||
@@ -931,13 +596,4 @@ public class PeopleSpaceUtilsTest extends SysuiTestCase {
|
||||
eq(shortcutId))).thenReturn(lastInteractionTimestamp);
|
||||
return convo;
|
||||
}
|
||||
|
||||
private ConversationChannel getConversationChannelWithoutTimestamp(String shortcutId)
|
||||
throws Exception {
|
||||
ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(mContext, shortcutId).setLongLabel(
|
||||
"name").build();
|
||||
ConversationChannel convo = new ConversationChannel(shortcutInfo, 0, null, null,
|
||||
0L, false);
|
||||
return convo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.people;
|
||||
|
||||
import static android.app.Notification.CATEGORY_MISSED_CALL;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_BIRTHDAY;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
|
||||
import static android.app.people.ConversationStatus.ACTIVITY_NEW_STORY;
|
||||
import static android.app.people.ConversationStatus.AVAILABILITY_AVAILABLE;
|
||||
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
|
||||
|
||||
import static com.android.systemui.people.widget.AppWidgetOptionsHelper.OPTIONS_PEOPLE_TILE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.people.ConversationStatus;
|
||||
import android.app.people.PeopleSpaceTile;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
public class PeopleTileViewHelperTest extends SysuiTestCase {
|
||||
|
||||
private static final String SHORTCUT_ID_1 = "101";
|
||||
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 String GAME_DESCRIPTION = "Playing a game!";
|
||||
private static final CharSequence MISSED_CALL = "Custom missed call message";
|
||||
private static final String NAME = "username";
|
||||
private static final PeopleSpaceTile PERSON_TILE_WITHOUT_NOTIFICATION =
|
||||
new PeopleSpaceTile
|
||||
.Builder(SHORTCUT_ID_1, NAME, ICON, new Intent())
|
||||
.setLastInteractionTimestamp(0L)
|
||||
.build();
|
||||
private static final PeopleSpaceTile PERSON_TILE =
|
||||
new PeopleSpaceTile
|
||||
.Builder(SHORTCUT_ID_1, NAME, ICON, new Intent())
|
||||
.setLastInteractionTimestamp(123L)
|
||||
.setNotificationKey(NOTIFICATION_KEY)
|
||||
.setNotificationContent(NOTIFICATION_CONTENT)
|
||||
.setNotificationDataUri(URI)
|
||||
.build();
|
||||
private static final ConversationStatus GAME_STATUS =
|
||||
new ConversationStatus
|
||||
.Builder(PERSON_TILE.getId(), ACTIVITY_GAME)
|
||||
.setDescription(GAME_DESCRIPTION)
|
||||
.build();
|
||||
private static final ConversationStatus NEW_STORY_WITH_AVAILABILITY =
|
||||
new ConversationStatus
|
||||
.Builder(PERSON_TILE.getId(), ACTIVITY_NEW_STORY)
|
||||
.setAvailability(AVAILABILITY_AVAILABLE)
|
||||
.build();
|
||||
|
||||
@Mock
|
||||
private Context mMockContext;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private Bundle mOptions;
|
||||
private PeopleTileViewHelper mPeopleTileViewHelper;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mOptions = new Bundle();
|
||||
mOptions.putParcelable(OPTIONS_PEOPLE_TILE, PERSON_TILE);
|
||||
|
||||
when(mMockContext.getString(R.string.birthday_status)).thenReturn(
|
||||
mContext.getString(R.string.birthday_status));
|
||||
when(mMockContext.getString(R.string.basic_status)).thenReturn(
|
||||
mContext.getString(R.string.basic_status));
|
||||
when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mMockContext.getString(R.string.over_timestamp)).thenReturn(
|
||||
mContext.getString(R.string.over_timestamp));
|
||||
Configuration configuration = mock(Configuration.class);
|
||||
DisplayMetrics displayMetrics = mock(DisplayMetrics.class);
|
||||
Resources resources = mock(Resources.class);
|
||||
when(mMockContext.getResources()).thenReturn(resources);
|
||||
when(resources.getConfiguration()).thenReturn(configuration);
|
||||
when(resources.getDisplayMetrics()).thenReturn(displayMetrics);
|
||||
TextView textView = mock(TextView.class);
|
||||
// when(new TextView(mMockContext)).thenReturn(textView);
|
||||
when(textView.getLineHeight()).thenReturn(16);
|
||||
when(mPackageManager.getApplicationIcon(anyString())).thenReturn(null);
|
||||
mPeopleTileViewHelper = new PeopleTileViewHelper(mContext,
|
||||
PERSON_TILE, 0, mOptions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithLastInteractionTime() {
|
||||
RemoteViews views = new PeopleTileViewHelper(mContext,
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
// Has last interaction.
|
||||
TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
|
||||
assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
|
||||
// No availability.
|
||||
assertEquals(View.GONE, result.findViewById(R.id.availability).getVisibility());
|
||||
// Shows person icon.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
|
||||
// No status.
|
||||
assertThat((View) result.findViewById(R.id.text_content)).isNull();
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_medium) - 1);
|
||||
RemoteViews smallView = new PeopleTileViewHelper(mContext,
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show name over predefined icon.
|
||||
assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Shows person icon.
|
||||
assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_large));
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_height_for_large));
|
||||
RemoteViews largeView = new PeopleTileViewHelper(mContext,
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION, 0, mOptions).getViews();
|
||||
View largeResult = largeView.apply(mContext, null);
|
||||
|
||||
name = (TextView) largeResult.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
// Has last interaction.
|
||||
lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
|
||||
assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
|
||||
// No availability.
|
||||
assertEquals(View.GONE, result.findViewById(R.id.availability).getVisibility());
|
||||
// Shows person icon.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
|
||||
// No status.
|
||||
assertThat((View) result.findViewById(R.id.text_content)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithGameTypeOnlyIsIgnored() {
|
||||
PeopleSpaceTile tileWithAvailabilityAndNewStory =
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
|
||||
Arrays.asList(NEW_STORY_WITH_AVAILABILITY,
|
||||
new ConversationStatus.Builder(
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
|
||||
ACTIVITY_GAME).build())).build();
|
||||
RemoteViews views = new PeopleTileViewHelper(mContext,
|
||||
tileWithAvailabilityAndNewStory, 0, mOptions).getViews();
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
// Has last interaction over status.
|
||||
TextView lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
|
||||
assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
|
||||
// Has availability.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
|
||||
// No status.
|
||||
assertThat((View) result.findViewById(R.id.text_content)).isNull();
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_medium) - 1);
|
||||
RemoteViews smallView = new PeopleTileViewHelper(mContext,
|
||||
tileWithAvailabilityAndNewStory, 0, mOptions).getViews();
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show name rather than game type.
|
||||
assertEquals(View.VISIBLE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_large));
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_height_for_large));
|
||||
RemoteViews largeView = new PeopleTileViewHelper(mContext,
|
||||
tileWithAvailabilityAndNewStory, 0, mOptions).getViews();
|
||||
View largeResult = largeView.apply(mContext, null);
|
||||
|
||||
name = (TextView) largeResult.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
// Has last interaction.
|
||||
lastInteraction = (TextView) result.findViewById(R.id.last_interaction);
|
||||
assertEquals(lastInteraction.getText(), mContext.getString(R.string.basic_status));
|
||||
// Has availability.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
|
||||
// Shows person icon.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
|
||||
// No status.
|
||||
assertThat((View) result.findViewById(R.id.text_content)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithBirthdayTypeOnlyIsNotIgnored() {
|
||||
PeopleSpaceTile tileWithStatusTemplate =
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
|
||||
Arrays.asList(
|
||||
NEW_STORY_WITH_AVAILABILITY, new ConversationStatus.Builder(
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION.getId(),
|
||||
ACTIVITY_BIRTHDAY).build())).build();
|
||||
RemoteViews views = new PeopleTileViewHelper(mContext,
|
||||
tileWithStatusTemplate, 0, mOptions).getViews();
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has availability.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
|
||||
// Has status text from backup text.
|
||||
TextView statusContent = (TextView) result.findViewById(R.id.text_content);
|
||||
assertEquals(View.VISIBLE, statusContent.getVisibility());
|
||||
assertEquals(statusContent.getText(), mContext.getString(R.string.birthday_status));
|
||||
assertThat(statusContent.getMaxLines()).isEqualTo(3);
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_medium) - 1);
|
||||
RemoteViews smallView = new PeopleTileViewHelper(mContext,
|
||||
tileWithStatusTemplate, 0, mOptions).getViews();
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show icon instead of name.
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_large));
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_height_for_large));
|
||||
RemoteViews largeView = new PeopleTileViewHelper(mContext,
|
||||
tileWithStatusTemplate, 0, mOptions).getViews();
|
||||
View largeResult = largeView.apply(mContext, null);
|
||||
|
||||
name = (TextView) largeResult.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
|
||||
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has availability.
|
||||
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
|
||||
// Has person icon.
|
||||
View personIcon = largeResult.findViewById(R.id.person_icon);
|
||||
assertEquals(View.VISIBLE, personIcon.getVisibility());
|
||||
// Has notification content.
|
||||
statusContent = (TextView) largeResult.findViewById(R.id.text_content);
|
||||
assertEquals(View.VISIBLE, statusContent.getVisibility());
|
||||
assertEquals(statusContent.getText(), mContext.getString(R.string.birthday_status));
|
||||
assertThat(statusContent.getMaxLines()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithStatusTemplate() {
|
||||
PeopleSpaceTile tileWithStatusTemplate =
|
||||
PERSON_TILE_WITHOUT_NOTIFICATION.toBuilder().setStatuses(
|
||||
Arrays.asList(GAME_STATUS,
|
||||
NEW_STORY_WITH_AVAILABILITY)).build();
|
||||
RemoteViews views = new PeopleTileViewHelper(mContext,
|
||||
tileWithStatusTemplate, 0, mOptions).getViews();
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has availability.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
|
||||
// Has status.
|
||||
TextView statusContent = (TextView) result.findViewById(R.id.text_content);
|
||||
assertEquals(statusContent.getText(), GAME_DESCRIPTION);
|
||||
assertThat(statusContent.getMaxLines()).isEqualTo(3);
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_medium) - 1);
|
||||
RemoteViews smallView = new PeopleTileViewHelper(mContext,
|
||||
tileWithStatusTemplate, 0, mOptions).getViews();
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show icon instead of name.
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_large));
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_height_for_large));
|
||||
RemoteViews largeView = new PeopleTileViewHelper(mContext,
|
||||
tileWithStatusTemplate, 0, mOptions).getViews();
|
||||
View largeResult = largeView.apply(mContext, null);
|
||||
|
||||
name = (TextView) largeResult.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
|
||||
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has availability.
|
||||
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
|
||||
// Has person icon.
|
||||
View personIcon = largeResult.findViewById(R.id.person_icon);
|
||||
assertEquals(View.VISIBLE, personIcon.getVisibility());
|
||||
// Has notification content.
|
||||
statusContent = (TextView) largeResult.findViewById(R.id.text_content);
|
||||
assertEquals(View.VISIBLE, statusContent.getVisibility());
|
||||
assertEquals(statusContent.getText(), GAME_DESCRIPTION);
|
||||
assertThat(statusContent.getMaxLines()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithMissedCallNotification() {
|
||||
PeopleSpaceTile tileWithMissedCallNotification = PERSON_TILE.toBuilder()
|
||||
.setNotificationDataUri(null)
|
||||
.setNotificationCategory(CATEGORY_MISSED_CALL)
|
||||
.setNotificationContent(MISSED_CALL)
|
||||
.build();
|
||||
RemoteViews views = new PeopleTileViewHelper(mContext,
|
||||
tileWithMissedCallNotification, 0, mOptions).getViews();
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has availability.
|
||||
assertEquals(View.GONE, result.findViewById(R.id.availability).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
|
||||
// Has missed call notification content.
|
||||
TextView statusContent = (TextView) result.findViewById(R.id.text_content);
|
||||
assertEquals(View.VISIBLE, statusContent.getVisibility());
|
||||
assertEquals(statusContent.getText(), MISSED_CALL);
|
||||
assertThat(statusContent.getMaxLines()).isEqualTo(3);
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_medium) - 1);
|
||||
RemoteViews smallView = new PeopleTileViewHelper(mContext,
|
||||
tileWithMissedCallNotification, 0, mOptions).getViews();
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show icon instead of name.
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE, smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_large));
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_height_for_large));
|
||||
RemoteViews largeView = new PeopleTileViewHelper(mContext,
|
||||
tileWithMissedCallNotification, 0, mOptions).getViews();
|
||||
View largeResult = largeView.apply(mContext, null);
|
||||
|
||||
name = (TextView) largeResult.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
|
||||
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has availability.
|
||||
assertEquals(View.GONE, largeResult.findViewById(R.id.availability).getVisibility());
|
||||
// Has person icon.
|
||||
View personIcon = largeResult.findViewById(R.id.person_icon);
|
||||
assertEquals(View.VISIBLE, personIcon.getVisibility());
|
||||
// Has notification content.
|
||||
statusContent = (TextView) largeResult.findViewById(R.id.text_content);
|
||||
assertEquals(View.VISIBLE, statusContent.getVisibility());
|
||||
assertEquals(statusContent.getText(), MISSED_CALL);
|
||||
assertThat(statusContent.getMaxLines()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateRemoteViewsWithNotificationTemplate() {
|
||||
PeopleSpaceTile tileWithStatusAndNotification = PERSON_TILE.toBuilder()
|
||||
.setNotificationDataUri(null)
|
||||
.setStatuses(Arrays.asList(GAME_STATUS,
|
||||
NEW_STORY_WITH_AVAILABILITY)).build();
|
||||
RemoteViews views = new PeopleTileViewHelper(mContext,
|
||||
tileWithStatusAndNotification, 0, mOptions).getViews();
|
||||
View result = views.apply(mContext, null);
|
||||
|
||||
TextView name = (TextView) result.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
assertEquals(View.GONE, result.findViewById(R.id.subtext).getVisibility());
|
||||
assertEquals(View.GONE, result.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has availability.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.availability).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE, result.findViewById(R.id.person_icon).getVisibility());
|
||||
// Has notification content.
|
||||
TextView statusContent = (TextView) result.findViewById(R.id.text_content);
|
||||
assertEquals(View.VISIBLE, statusContent.getVisibility());
|
||||
assertEquals(statusContent.getText(), NOTIFICATION_CONTENT);
|
||||
assertThat(statusContent.getMaxLines()).isEqualTo(3);
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_medium) - 1);
|
||||
RemoteViews smallView = new PeopleTileViewHelper(mContext,
|
||||
tileWithStatusAndNotification, 0, mOptions).getViews();
|
||||
View smallResult = smallView.apply(mContext, null);
|
||||
|
||||
// Show icon instead of name.
|
||||
assertEquals(View.GONE, smallResult.findViewById(R.id.name).getVisibility());
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has person icon.
|
||||
assertEquals(View.VISIBLE,
|
||||
smallResult.findViewById(R.id.person_icon).getVisibility());
|
||||
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_width_for_large));
|
||||
mOptions.putInt(OPTION_APPWIDGET_MIN_WIDTH,
|
||||
getSizeInDp(R.dimen.required_height_for_large));
|
||||
RemoteViews largeView = new PeopleTileViewHelper(mContext,
|
||||
tileWithStatusAndNotification, 0, mOptions).getViews();
|
||||
View largeResult = largeView.apply(mContext, null);
|
||||
|
||||
name = (TextView) largeResult.findViewById(R.id.name);
|
||||
assertEquals(name.getText(), NAME);
|
||||
assertEquals(View.GONE, largeResult.findViewById(R.id.subtext).getVisibility());
|
||||
assertEquals(View.GONE, largeResult.findViewById(R.id.predefined_icon).getVisibility());
|
||||
// Has availability.
|
||||
assertEquals(View.VISIBLE, largeResult.findViewById(R.id.availability).getVisibility());
|
||||
// Has person icon.
|
||||
View personIcon = largeResult.findViewById(R.id.person_icon);
|
||||
assertEquals(View.VISIBLE, personIcon.getVisibility());
|
||||
// Has notification content.
|
||||
statusContent = (TextView) largeResult.findViewById(R.id.text_content);
|
||||
assertEquals(View.VISIBLE, statusContent.getVisibility());
|
||||
assertEquals(statusContent.getText(), NOTIFICATION_CONTENT);
|
||||
assertThat(statusContent.getMaxLines()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageNoPunctuation() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test");
|
||||
|
||||
assertThat(backgroundText).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageSingleExclamation() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test!");
|
||||
|
||||
assertThat(backgroundText).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageSingleQuestion() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("?test");
|
||||
|
||||
assertThat(backgroundText).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageSeparatedMarks() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test! right!");
|
||||
|
||||
assertThat(backgroundText).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageDoubleExclamation() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("!!test");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageDoubleQuestion() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test??");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageMixed() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage("test?!");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageMixedInTheMiddle() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage(
|
||||
"test!? in the middle");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageMixedDifferentOrder() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage(
|
||||
"test!? in the middle");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageMultiple() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage(
|
||||
"test!?!!? in the middle");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageQuestionFirst() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage(
|
||||
"test?? in the middle!!");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBackgroundTextFromMessageExclamationFirst() {
|
||||
String backgroundText = mPeopleTileViewHelper.getBackgroundTextFromMessage(
|
||||
"test!! in the middle??");
|
||||
|
||||
assertThat(backgroundText).isEqualTo("!");
|
||||
}
|
||||
|
||||
private int getSizeInDp(int dimenResourceId) {
|
||||
return (int) (mContext.getResources().getDimension(dimenResourceId)
|
||||
/ mContext.getResources().getDisplayMetrics().density);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user