Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 79477111 authored by Flavio Fiszman's avatar Flavio Fiszman Committed by Android (Google) Code Review
Browse files

Merge "Update People Space widgets on posted notifications."

parents ef3da571 40d69a63
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ import android.view.accessibility.AccessibilityManager;
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;
@@ -188,6 +189,13 @@ public class FrameworkServicesModule {
        return context.getSystemService(InputMethodManager.class);
    }

    @Provides
    @Singleton
    static IAppWidgetService provideIAppWidgetService() {
        return IAppWidgetService.Stub.asInterface(
                ServiceManager.getService(Context.APPWIDGET_SERVICE));
    }

    @Provides
    @Singleton
    static IPackageManager provideIPackageManager() {
+135 −0
Original line number Diff line number Diff line
/*
 * 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();
        }
    };

}
+5 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.util.Log;
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 @@ public class NotifPipelineInitializer implements Dumpable {
    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 @@ public class NotifPipelineInitializer implements Dumpable {
            ShadeListBuilder listBuilder,
            NotifCoordinators notifCoordinators,
            NotifInflaterImpl notifInflater,
            PeopleSpaceWidgetManager peopleSpaceWidgetManager,
            DumpManager dumpManager,
            ShadeViewManagerFactory shadeViewManagerFactory,
            FeatureFlags featureFlags) {
@@ -72,6 +75,7 @@ public class NotifPipelineInitializer implements Dumpable {
        mNotifPluggableCoordinators = notifCoordinators;
        mDumpManager = dumpManager;
        mNotifInflater = notifInflater;
        mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
        mShadeViewManagerFactory = shadeViewManagerFactory;
        mFeatureFlags = featureFlags;
    }
@@ -99,6 +103,7 @@ public class NotifPipelineInitializer implements Dumpable {
        mListBuilder.attach(mNotifCollection);
        mNotifCollection.attach(mGroupCoalescer);
        mGroupCoalescer.attach(notificationService);
        mPeopleSpaceWidgetManager.attach(notificationService);

        Log.d(TAG, "Notif pipeline initialized");
    }
+152 −0
Original line number Diff line number Diff line
/*
 * 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());
    }
}