Merge "Update People Space widgets on posted notifications."
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 79925ba..9705551 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -65,6 +65,7 @@
import android.view.inputmethod.InputMethodManager;
import com.android.internal.app.IBatteryStats;
+import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -190,6 +191,13 @@
@Provides
@Singleton
+ static IAppWidgetService provideIAppWidgetService() {
+ return IAppWidgetService.Stub.asInterface(
+ ServiceManager.getService(Context.APPWIDGET_SERVICE));
+ }
+
+ @Provides
+ @Singleton
static IPackageManager provideIPackageManager() {
return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
}
diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
new file mode 100644
index 0000000..8415368
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.people.widget;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.appwidget.IAppWidgetService;
+import com.android.systemui.R;
+import com.android.systemui.people.PeopleSpaceUtils;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** Manager for People Space widget. */
+@Singleton
+public class PeopleSpaceWidgetManager {
+ private static final String TAG = "PeopleSpaceWidgetMgr";
+ private static final boolean DEBUG = PeopleSpaceUtils.DEBUG;
+
+ private final Context mContext;
+ private IAppWidgetService mAppWidgetManager;
+
+ @Inject
+ public PeopleSpaceWidgetManager(Context context, IAppWidgetService appWidgetService) {
+ if (DEBUG) Log.d(TAG, "constructor");
+ mContext = context;
+ mAppWidgetManager = appWidgetService;
+ }
+
+ /** Constructor used for testing. */
+ @VisibleForTesting
+ protected PeopleSpaceWidgetManager(Context context) {
+ if (DEBUG) Log.d(TAG, "constructor");
+ mContext = context;
+ mAppWidgetManager = IAppWidgetService.Stub.asInterface(
+ ServiceManager.getService(Context.APPWIDGET_SERVICE));
+ }
+
+ /** AppWidgetManager setter used for testing. */
+ @VisibleForTesting
+ protected void setAppWidgetManager(IAppWidgetService appWidgetService) {
+ mAppWidgetManager = appWidgetService;
+ }
+
+ /** Updates People Space widgets. */
+ public void updateWidgets() {
+ try {
+ if (DEBUG) Log.d(TAG, "updateWidgets called");
+ int[] widgetIds = mAppWidgetManager.getAppWidgetIds(
+ new ComponentName(mContext, PeopleSpaceWidgetProvider.class)
+ );
+
+ if (widgetIds.length == 0) {
+ if (DEBUG) Log.d(TAG, "no widgets to update");
+ return;
+ }
+
+ if (DEBUG) Log.d(TAG, "updating " + widgetIds.length + " widgets");
+ mAppWidgetManager
+ .notifyAppWidgetViewDataChanged(mContext.getOpPackageName(), widgetIds,
+ R.id.widget_list_view);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception: " + e);
+ }
+ }
+
+ /**
+ * Attaches the manager to the pipeline, making it ready to receive events. Should only be
+ * called once.
+ */
+ public void attach(NotificationListener listenerService) {
+ if (DEBUG) Log.d(TAG, "attach");
+ listenerService.addNotificationHandler(mListener);
+ }
+
+ private final NotificationHandler mListener = new NotificationHandler() {
+ @Override
+ public void onNotificationPosted(
+ StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) {
+ if (DEBUG) Log.d(TAG, "onNotificationPosted");
+ updateWidgets();
+ }
+
+ @Override
+ public void onNotificationRemoved(
+ StatusBarNotification sbn,
+ NotificationListenerService.RankingMap rankingMap
+ ) {
+ if (DEBUG) Log.d(TAG, "onNotificationRemoved");
+ updateWidgets();
+ }
+
+ @Override
+ public void onNotificationRemoved(
+ StatusBarNotification sbn,
+ NotificationListenerService.RankingMap rankingMap,
+ int reason) {
+ if (DEBUG) Log.d(TAG, "onNotificationRemoved with reason " + reason);
+ updateWidgets();
+ }
+
+ @Override
+ public void onNotificationRankingUpdate(
+ NotificationListenerService.RankingMap rankingMap) { }
+
+ @Override
+ public void onNotificationsInitialized() {
+ if (DEBUG) Log.d(TAG, "onNotificationsInitialized");
+ updateWidgets();
+ }
+ };
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index db49e44..4fde118 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -21,6 +21,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
@@ -49,6 +50,7 @@
private final ShadeListBuilder mListBuilder;
private final NotifCoordinators mNotifPluggableCoordinators;
private final NotifInflaterImpl mNotifInflater;
+ private final PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
private final DumpManager mDumpManager;
private final ShadeViewManagerFactory mShadeViewManagerFactory;
private final FeatureFlags mFeatureFlags;
@@ -62,6 +64,7 @@
ShadeListBuilder listBuilder,
NotifCoordinators notifCoordinators,
NotifInflaterImpl notifInflater,
+ PeopleSpaceWidgetManager peopleSpaceWidgetManager,
DumpManager dumpManager,
ShadeViewManagerFactory shadeViewManagerFactory,
FeatureFlags featureFlags) {
@@ -72,6 +75,7 @@
mNotifPluggableCoordinators = notifCoordinators;
mDumpManager = dumpManager;
mNotifInflater = notifInflater;
+ mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
mShadeViewManagerFactory = shadeViewManagerFactory;
mFeatureFlags = featureFlags;
}
@@ -99,6 +103,7 @@
mListBuilder.attach(mNotifCollection);
mNotifCollection.attach(mGroupCoalescer);
mGroupCoalescer.attach(notificationService);
+ mPeopleSpaceWidgetManager.attach(notificationService);
Log.d(TAG, "Notif pipeline initialized");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
new file mode 100644
index 0000000..8ceebeb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.people.widget;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import static java.util.Objects.requireNonNull;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.appwidget.IAppWidgetService;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
+import com.android.systemui.statusbar.notification.collection.NoManSimulator;
+import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
+ private static final long MIN_LINGER_DURATION = 5;
+
+ private static final String TEST_PACKAGE_A = "com.test.package_a";
+ private static final String TEST_PACKAGE_B = "com.test.package_b";
+
+ private PeopleSpaceWidgetManager mManager;
+
+ @Mock private NotificationListener mListenerService;
+ @Mock private IAppWidgetService mIAppWidgetService;
+ @Mock private Context mContext;
+
+ @Captor private ArgumentCaptor<NotificationHandler> mListenerCaptor;
+
+ private final NoManSimulator mNoMan = new NoManSimulator();
+ private final FakeSystemClock mClock = new FakeSystemClock();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mManager =
+ new PeopleSpaceWidgetManager(mContext);
+ mManager.setAppWidgetManager(mIAppWidgetService);
+ mManager.attach(mListenerService);
+
+ verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
+ NotificationHandler serviceListener = requireNonNull(mListenerCaptor.getValue());
+ mNoMan.addListener(serviceListener);
+ }
+
+
+ @Test
+ public void testDoNotNotifyAppWidgetIfNoWidgets() throws RemoteException {
+ 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(mIAppWidgetService, never()).notifyAppWidgetViewDataChanged(any(), any(), anyInt());
+
+ }
+
+ @Test
+ public void testNotifyAppWidgetIfWidgets() throws RemoteException {
+ int[] widgetIdsArray = {1};
+ 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(mIAppWidgetService, times(1))
+ .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
+
+ }
+
+ @Test
+ public void testNotifyAppWidgetTwiceIfTwoNotifications() throws RemoteException {
+ int[] widgetIdsArray = {1, 2};
+ 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, times(2))
+ .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
+ }
+
+ @Test
+ public void testNotifyAppWidgetTwiceIfNotificationPostedAndRemoved() throws RemoteException {
+ int[] widgetIdsArray = {1, 2};
+ when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
+
+ NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+ .setPkg(TEST_PACKAGE_A)
+ .setId(1));
+ mClock.advanceTime(4);
+ NotifEvent notif1b = mNoMan.retractNotif(notif1.sbn, 0);
+
+ verify(mIAppWidgetService, times(2)).getAppWidgetIds(any());
+ verify(mIAppWidgetService, times(2))
+ .notifyAppWidgetViewDataChanged(any(), eq(widgetIdsArray), anyInt());
+ }
+}