Merge "Support single conversation in People Space widget"

This commit is contained in:
Anna Zappone
2020-12-08 10:58:52 +00:00
committed by Android (Google) Code Review
15 changed files with 438 additions and 110 deletions

View File

@@ -14484,10 +14484,11 @@ public final class Settings {
public static final String SHOW_PEOPLE_SPACE = "show_people_space";
/**
* Which types of conversations to show in People Space.
* Which types of conversation(s) to show in People Space.
* Values are:
* 0: All conversations (default)
* 0: Single user-selected conversation (default)
* 1: Priority conversations only
* 2: All conversations
* @hide
*/
public static final String PEOPLE_SPACE_CONVERSATION_TYPE =

View File

@@ -576,6 +576,7 @@
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>

View File

@@ -0,0 +1,19 @@
<!--
~ Copyright (C) 2020 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?android:attr/colorAccent" />
<corners android:radius="@dimen/people_space_widget_radius" />
</shape>

View File

@@ -17,8 +17,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light">
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/people_space_layout"
@@ -30,16 +29,15 @@
android:clipChildren="false"
android:clipToPadding="false">
<ImageView
android:id="@+id/people_space_cloud"
android:layout_width="67dp"
android:layout_height="67dp"
<TextView
android:id="@+id/select_conversation"
android:text="@string/select_conversation_text"
android:layout_width="match_parent"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textSize="24sp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:scaleType="fitCenter"
android:focusable="false"
android:paddingBottom="16dp"
android:tint="?android:attr/colorControlNormal"
android:src="@drawable/cloud" />
android:paddingBottom="16dp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -20,46 +20,53 @@
android:orientation="vertical">
<LinearLayout
android:background="@drawable/people_space_tile_view_card"
android:orientation="vertical"
android:padding="16dp"
android:layout_marginBottom="24dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/people_space_activity_card"
android:padding="12dp"
android:elevation="4dp"
android:gravity="start">
android:layout_marginBottom="12dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/tile_view_package_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="end" />
<ImageView
android:id="@+id/tile_view_person_icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="start" />
<TextView
android:id="@+id/tile_view_name"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:paddingVertical="4dp"
android:textSize="22sp"
android:textColor="?android:attr/textColorPrimary"
android:layout_width="wrap_content"
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start" />
android:gravity="start">
<TextView
android:id="@+id/tile_view_status"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:paddingVertical="4dp"
android:textSize="16sp"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:layout_gravity="start" />
<ImageView
android:id="@+id/tile_view_person_icon"
android:layout_width="48dp"
android:layout_height="48dp" />
<ImageView
android:id="@+id/tile_view_package_icon"
android:layout_width="16dp"
android:layout_marginStart="-8dp"
android:layout_height="16dp" />
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tile_view_name"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:paddingHorizontal="16dp"
android:textSize="22sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tile_view_status"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:paddingVertical="4dp"
android:textSize="12sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -22,6 +22,6 @@
android:background="@drawable/people_space_widget_background"
android:clipChildren="false"
android:clipToPadding="false"
android:padding="5dp"
android:padding="2dp"
android:divider="@null"
android:dividerHeight="0dp"/>

View File

@@ -22,38 +22,57 @@
android:background="@drawable/people_space_tile_view_card"
android:id="@+id/item"
android:orientation="vertical"
android:padding="6dp"
android:layout_marginBottom="6dp"
android:padding="4dp"
android:layout_marginBottom="2dp"
android:elevation="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start">
<ImageView
android:id="@+id/package_icon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="end" />
<ImageView
android:id="@+id/person_icon"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="start" />
<TextView
android:id="@+id/name"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textSize="18sp"
android:textColor="?android:attr/textColorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start" />
<TextView
android:id="@+id/status"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:paddingVertical="2dp"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start" />
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/person_icon"
android:layout_width="42dp"
android:layout_height="42dp" />
<ImageView
android:id="@+id/package_icon"
android:gravity="bottom|end"
android:layout_width="14dp"
android:layout_marginStart="-8dp"
android:layout_height="14dp" />
<LinearLayout
android:orientation="vertical"
android:paddingStart="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/name"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:maxLines="1"
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/status"
android:textColor="?android:attr/textColorSecondary"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
android:paddingVertical="2dp"
android:textSize="12sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="3"
android:ellipsize="end" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -2824,13 +2824,15 @@
<string name="build_number_copy_toast">Build number copied to clipboard.</string>
<!-- Status for last interaction with exact time [CHAR LIMIT=120] -->
<string name="last_interaction_status" translatable="false">You last chatted <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
<string name="last_interaction_status" translatable="false">Last chatted <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
<!-- Status for last interaction when less than a certain time window [CHAR LIMIT=120] -->
<string name="last_interaction_status_less_than" translatable="false">You last chatted less than <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
<string name="last_interaction_status_less_than" translatable="false">Last chatted less than <xliff:g id="duration" example="5 hours">%1$s</xliff:g> ago</string>
<!-- Status for last interaction when over a certain time window [CHAR LIMIT=120] -->
<string name="last_interaction_status_over" translatable="false">You last chatted over <xliff:g id="duration" example="1 week">%1$s</xliff:g> ago</string>
<string name="last_interaction_status_over" translatable="false">Last chatted over <xliff:g id="duration" example="1 week">%1$s</xliff:g> ago</string>
<!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
<string name="basic_status" translatable="false">Open conversation</string>
<!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
<string name="select_conversation_text" translatable="false">Select one conversation to show in your widget:</string>
<!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
[CHAR LIMIT=NONE] -->

View File

@@ -15,10 +15,11 @@
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="72dp"
android:minHeight="150dp"
android:minWidth="180dp"
android:minHeight="40dp"
android:updatePeriodMillis="60000"
android:previewImage="@drawable/cloud"
android:previewImage="@drawable/ic_android"
android:resizeMode="horizontal|vertical"
android:configure="com.android.systemui.people.PeopleSpaceActivity"
android:initialLayout="@layout/people_space_widget">
</appwidget-provider>

View File

@@ -16,19 +16,30 @@
package com.android.systemui.people;
import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
import android.app.Activity;
import android.app.INotificationManager;
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
import android.view.ViewGroup;
import androidx.preference.PreferenceManager;
import com.android.systemui.R;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import java.util.List;
import java.util.Map;
@@ -46,6 +57,9 @@ public class PeopleSpaceActivity extends Activity {
private PackageManager mPackageManager;
private LauncherApps mLauncherApps;
private Context mContext;
private AppWidgetManager mAppWidgetManager;
private int mAppWidgetId;
private boolean mShowSingleConversation;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -59,7 +73,18 @@ public class PeopleSpaceActivity extends Activity {
mPeopleManager = IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE));
mLauncherApps = mContext.getSystemService(LauncherApps.class);
mAppWidgetManager = AppWidgetManager.getInstance(mContext);
setTileViewsWithPriorityConversations();
mAppWidgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID,
INVALID_APPWIDGET_ID);
mShowSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
// Finish the configuration activity immediately if a widget is added for multiple
// conversations. If the mAppWidgetId is INVALID, then the activity wasn't launched as a
// widget configuration activity.
if (!mShowSingleConversation && mAppWidgetId != INVALID_APPWIDGET_ID) {
finishActivity();
}
}
/**
@@ -93,12 +118,39 @@ public class PeopleSpaceActivity extends Activity {
tileView.setName(tile.getUserName().toString());
tileView.setPackageIcon(mPackageManager.getApplicationIcon(pkg));
tileView.setPersonIcon(tile.getUserIcon());
tileView.setOnClickListener(mLauncherApps, tile);
tileView.setOnClickListener(v -> storeWidgetConfiguration(tile));
} catch (Exception e) {
Log.e(TAG, "Couldn't retrieve shortcut information", e);
}
}
/** Stores the user selected configuration for {@code mAppWidgetId}. */
private void storeWidgetConfiguration(PeopleSpaceTile tile) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sp.edit();
if (PeopleSpaceUtils.DEBUG) {
Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: "
+ tile.getId() + " for widget ID: "
+ mAppWidgetId);
}
editor.putString(String.valueOf(mAppWidgetId), tile.getId());
editor.commit();
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
int[] widgetIds = appWidgetManager.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class));
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds, mAppWidgetManager,
mNotificationManager);
finishActivity();
}
/** Finish activity with a successful widget configuration result. */
private void finishActivity() {
Intent resultValue = new Intent();
resultValue.putExtra(EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
}
@Override
protected void onResume() {
super.onResume();

View File

@@ -84,4 +84,9 @@ public class PeopleSpaceTileView extends LinearLayout {
launcherApps.startShortcut(tile.getPackageName(), tile.getId(), null, null,
UserHandle.getUserHandleForUid(tile.getUid())));
}
/** Sets the click listener of the tile directly. */
public void setOnClickListener(OnClickListener onClickListener) {
mTileView.setOnClickListener(onClickListener);
}
}

View File

@@ -17,11 +17,17 @@
package com.android.systemui.people;
import android.app.INotificationManager;
import android.app.PendingIntent;
import android.app.people.ConversationChannel;
import android.app.people.IPeopleManager;
import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetHost;
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.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
@@ -29,17 +35,24 @@ import android.graphics.drawable.Drawable;
import android.icu.text.MeasureFormat;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
import android.util.Log;
import android.widget.RemoteViews;
import androidx.preference.PreferenceManager;
import com.android.systemui.R;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import java.time.Duration;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -57,14 +70,15 @@ public class PeopleSpaceUtils {
Context context, INotificationManager notificationManager, IPeopleManager peopleManager,
LauncherApps launcherApps)
throws Exception {
boolean showAllConversations = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
boolean showOnlyPriority = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 1;
List<ConversationChannelWrapper> conversations = notificationManager.getConversations(
true).getList();
List<Map.Entry<Long, PeopleSpaceTile>> tiles = getSortedTiles(peopleManager,
conversations.stream().map(c ->
new PeopleSpaceTile.Builder(c.getShortcutInfo(), launcherApps).build()));
if (showAllConversations) {
if (!showOnlyPriority) {
if (DEBUG) Log.d(TAG, "Add recent conversations");
List<ConversationChannel> recentConversations =
peopleManager.getRecentConversations().getList();
List<Map.Entry<Long, PeopleSpaceTile>> recentTiles =
@@ -77,6 +91,73 @@ public class PeopleSpaceUtils {
return tiles;
}
/** Updates {@code appWidgetIds} with their associated conversation stored. */
public static void updateSingleConversationWidgets(Context context, int[] appWidgetIds,
AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
PackageManager mPackageManager = context.getPackageManager();
IPeopleManager mPeopleManager = IPeopleManager.Stub.asInterface(
ServiceManager.getService(Context.PEOPLE_SERVICE));
LauncherApps mLauncherApps = context.getSystemService(LauncherApps.class);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
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);
try {
List<Map.Entry<Long, PeopleSpaceTile>> shortcutInfos =
PeopleSpaceUtils.getTiles(
context, notificationManager,
mPeopleManager, mLauncherApps);
for (int appWidgetId : appWidgetIds) {
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.people_space_widget_item);
String shortcutId = sp.getString(String.valueOf(appWidgetId), null);
if (DEBUG) {
Log.d(TAG, "Set widget: " + appWidgetId + " with shortcut ID: " + shortcutId);
}
Optional<Map.Entry<Long, PeopleSpaceTile>> entry = shortcutInfos.stream().filter(
e -> e.getValue().getId().equals(shortcutId)).findFirst();
if (!entry.isPresent() || shortcutId == null) {
if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
AppWidgetHost host = new AppWidgetHost(context, 0);
host.deleteAppWidgetId(appWidgetId);
continue;
}
PeopleSpaceTile tile = entry.get().getValue();
String status = PeopleSpaceUtils.getLastInteractionString(context,
entry.get().getKey());
views.setTextViewText(R.id.status, status);
views.setTextViewText(R.id.name, tile.getUserName().toString());
activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId());
activityIntent.putExtra(
PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName());
activityIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid());
views.setOnClickPendingIntent(R.id.item, PendingIntent.getActivity(
context,
appWidgetId,
activityIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE));
views.setImageViewBitmap(
R.id.package_icon,
PeopleSpaceUtils.convertDrawableToBitmap(
mPackageManager.getApplicationIcon(tile.getPackageName())
)
);
views.setImageViewIcon(R.id.person_icon, tile.getUserIcon());
// Tell the AppWidgetManager to perform an update on the current app widget.
appWidgetManager.updateAppWidget(appWidgetId, views);
}
} catch (Exception e) {
Log.e(TAG, "Failed to retrieve conversations to set tiles");
}
}
/** Returns a list sorted by ascending last interaction time from {@code stream}. */
private static List<Map.Entry<Long, PeopleSpaceTile>> getSortedTiles(
IPeopleManager peopleManager, Stream<PeopleSpaceTile> stream) {

View File

@@ -16,11 +16,14 @@
package com.android.systemui.people.widget;
import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
@@ -42,13 +45,18 @@ public class PeopleSpaceWidgetManager {
private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
private final Context mContext;
private IAppWidgetService mAppWidgetManager;
private IAppWidgetService mAppWidgetService;
private AppWidgetManager mAppWidgetManager;
private INotificationManager mNotificationManager;
@Inject
public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
if (DEBUG) Log.d(TAG, "constructor");
mContext = context;
mAppWidgetManager = appWidgetService;
mAppWidgetService = appWidgetService;
mAppWidgetManager = AppWidgetManager.getInstance(context);
mNotificationManager = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
}
/** Constructor used for testing. */
@@ -56,21 +64,24 @@ public class PeopleSpaceWidgetManager {
protected PeopleSpaceWidgetManager(Context context) {
if (DEBUG) Log.d(TAG, "constructor");
mContext = context;
mAppWidgetManager = IAppWidgetService.Stub.asInterface(
mAppWidgetService = IAppWidgetService.Stub.asInterface(
ServiceManager.getService(Context.APPWIDGET_SERVICE));
}
/** AppWidgetManager setter used for testing. */
@VisibleForTesting
protected void setAppWidgetManager(IAppWidgetService appWidgetService) {
mAppWidgetManager = appWidgetService;
protected void setAppWidgetManager(IAppWidgetService appWidgetService,
AppWidgetManager appWidgetManager, INotificationManager notificationManager) {
mAppWidgetService = appWidgetService;
mAppWidgetManager = appWidgetManager;
mNotificationManager = notificationManager;
}
/** Updates People Space widgets. */
public void updateWidgets() {
try {
if (DEBUG) Log.d(TAG, "updateWidgets called");
int[] widgetIds = mAppWidgetManager.getAppWidgetIds(
int[] widgetIds = mAppWidgetService.getAppWidgetIds(
new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
);
@@ -80,9 +91,16 @@ public class PeopleSpaceWidgetManager {
}
if (DEBUG) Log.d(TAG, "updating " + widgetIds.length + " widgets");
mAppWidgetManager
.notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
R.id.widget_list_view);
boolean showSingleConversation = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(mContext, widgetIds,
mAppWidgetManager, mNotificationManager);
} else {
mAppWidgetService
.notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
R.id.widget_list_view);
}
} catch (Exception e) {
Log.e(TAG, "Exception: " + e);
}
@@ -125,7 +143,8 @@ public class PeopleSpaceWidgetManager {
@Override
public void onNotificationRankingUpdate(
NotificationListenerService.RankingMap rankingMap) { }
NotificationListenerService.RankingMap rankingMap) {
}
@Override
public void onNotificationsInitialized() {

View File

@@ -16,11 +16,14 @@
package com.android.systemui.people.widget;
import android.app.INotificationManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
import android.widget.RemoteViews;
@@ -41,6 +44,14 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
super.onUpdate(context, appWidgetManager, appWidgetIds);
if (DEBUG) Log.d(TAG, "onUpdate called");
boolean showSingleConversation = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0;
if (showSingleConversation) {
PeopleSpaceUtils.updateSingleConversationWidgets(context, appWidgetIds,
appWidgetManager, INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE)));
return;
}
// Perform this loop procedure for each App Widget that belongs to this provider
for (int appWidgetId : appWidgetIds) {
RemoteViews views =

View File

@@ -29,12 +29,20 @@ import static org.mockito.Mockito.when;
import static java.util.Objects.requireNonNull;
import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.content.Context;
import android.appwidget.AppWidgetManager;
import android.content.SharedPreferences;
import android.content.pm.ParceledListSlice;
import android.content.pm.ShortcutInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
import android.testing.AndroidTestingRunner;
import android.widget.RemoteViews;
import androidx.preference.PreferenceManager;
import androidx.test.filters.SmallTest;
import com.android.internal.appwidget.IAppWidgetService;
@@ -54,6 +62,9 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@@ -65,14 +76,23 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
private static final String TEST_CHANNEL_NAME = "channel_name";
private static final String TEST_PARENT_CHANNEL_ID = "parent_channel_id";
private static final String TEST_CONVERSATION_ID = "conversation_id";
private static final int WIDGET_ID_WITH_SHORTCUT = 1;
private static final int WIDGET_ID_WITHOUT_SHORTCUT = 2;
private static final String SHORTCUT_ID = "101";
private PeopleSpaceWidgetManager mManager;
@Mock private NotificationListener mListenerService;
@Mock private IAppWidgetService mIAppWidgetService;
@Mock private Context mContext;
@Mock
private NotificationListener mListenerService;
@Mock
private IAppWidgetService mIAppWidgetService;
@Mock
private AppWidgetManager mAppWidgetManager;
@Mock
private INotificationManager mINotificationManager;
@Captor private ArgumentCaptor<NotificationHandler> mListenerCaptor;
@Captor
private ArgumentCaptor<NotificationHandler> mListenerCaptor;
private final NoManSimulator mNoMan = new NoManSimulator();
private final FakeSystemClock mClock = new FakeSystemClock();
@@ -80,18 +100,18 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mManager =
new PeopleSpaceWidgetManager(mContext);
mManager.setAppWidgetManager(mIAppWidgetService);
mManager.setAppWidgetManager(mIAppWidgetService, mAppWidgetManager, mINotificationManager);
mManager.attach(mListenerService);
verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
mNoMan.addListener(serviceListener);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 2);
}
@Test
public void testDoNotNotifyAppWidgetIfNoWidgets() throws RemoteException {
int[] widgetIdsArray = {};
@@ -105,7 +125,24 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
verify(mIAppWidgetService, times(1)).getAppWidgetIds(any());
verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
}
@Test
public void testDoNotNotifySingleConversationAppWidgetIfNoWidgets() throws RemoteException {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
int[] widgetIdsArray = {};
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
NotifEvent notif1 = mNoMan.postNotif(
new NotificationEntryBuilder()
.setId(0)
.setPkg(TEST_PACKAGE_A));
mClock.advanceTime(MIN_LINGER_DURATION);
verify(mIAppWidgetService, times(1)).getAppWidgetIds(any());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(), any(RemoteViews.class));
verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
}
@Test
@@ -122,7 +159,66 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
verify(mIAppWidgetService, times(1)).getAppWidgetIds(any());
verify(mIAppWidgetService, times(1))
.notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
verify(mIAppWidgetService, never()).updateAppWidgetIds(any(), any(),
any(RemoteViews.class));
}
@Test
public void testNotifySingleConversationAppWidgetOnceIfNotificationPosted()
throws RemoteException {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
when(mINotificationManager.getConversations(true)).thenReturn(
new ParceledListSlice(getConversationWithShortcutId()));
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sp.edit();
editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID);
editor.commit();
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_A)
.setId(1));
verify(mIAppWidgetService, times(1)).getAppWidgetIds(any());
verify(mIAppWidgetService, never())
.notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
verify(mAppWidgetManager, times(1)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any(RemoteViews.class));
verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
any(RemoteViews.class));
}
@Test
public void testNotifySingleConversationAppWidgetTwiceIfTwoNotificationsPosted()
throws RemoteException {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0);
when(mINotificationManager.getConversations(true)).thenReturn(
new ParceledListSlice(getConversationWithShortcutId()));
int[] widgetIdsArray = {WIDGET_ID_WITH_SHORTCUT, WIDGET_ID_WITHOUT_SHORTCUT};
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sp.edit();
editor.putString(String.valueOf(WIDGET_ID_WITH_SHORTCUT), SHORTCUT_ID);
editor.commit();
when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_A)
.setId(1));
mClock.advanceTime(4);
NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_B)
.setId(2));
verify(mIAppWidgetService, times(2)).getAppWidgetIds(any());
verify(mIAppWidgetService, never())
.notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
verify(mAppWidgetManager, times(2)).updateAppWidget(eq(WIDGET_ID_WITH_SHORTCUT),
any(RemoteViews.class));
verify(mAppWidgetManager, never()).updateAppWidget(eq(WIDGET_ID_WITHOUT_SHORTCUT),
any(RemoteViews.class));
}
@Test
@@ -141,6 +237,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
verify(mIAppWidgetService, times(2)).getAppWidgetIds(any());
verify(mIAppWidgetService, times(2))
.notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
any(RemoteViews.class));
}
@Test
@@ -157,6 +255,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
verify(mIAppWidgetService, times(2)).getAppWidgetIds(any());
verify(mIAppWidgetService, times(2))
.notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
any(RemoteViews.class));
}
@Test
@@ -173,7 +273,8 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
verify(mIAppWidgetService, never()).getAppWidgetIds(any());
verify(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
any(RemoteViews.class));
}
@Test
@@ -192,6 +293,17 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
verify(mIAppWidgetService, times(1)).getAppWidgetIds(any());
verify(mIAppWidgetService, times(1))
.notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
verify(mAppWidgetManager, never()).updateAppWidget(anyInt(),
any(RemoteViews.class));
}
/** Returns a list of a single conversation associated with {@code SHORTCUT_ID}. */
private List<ConversationChannelWrapper> getConversationWithShortcutId() {
List<ConversationChannelWrapper> convos = new ArrayList<>();
ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
convo1.setShortcutInfo(new ShortcutInfo.Builder(mContext, SHORTCUT_ID).setLongLabel(
"name").build());
convos.add(convo1);
return convos;
}
}