Merge "Updated NotificationPanelViewController to listen to animateExpandNotificationsPanel and animateCollapsePanels." into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
489dd7154c
@@ -48,17 +48,21 @@ import com.android.systemui.car.CarServiceProvider;
|
||||
import com.android.systemui.car.window.OverlayPanelViewController;
|
||||
import com.android.systemui.car.window.OverlayViewGlobalStateController;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
import com.android.systemui.statusbar.FlingAnimationUtils;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/** View controller for the notification panel. */
|
||||
@Singleton
|
||||
public class NotificationPanelViewController extends OverlayPanelViewController {
|
||||
public class NotificationPanelViewController extends OverlayPanelViewController
|
||||
implements CommandQueue.Callbacks {
|
||||
|
||||
private static final boolean DEBUG = true;
|
||||
private static final String TAG = "NotificationPanelViewController";
|
||||
@@ -68,12 +72,14 @@ public class NotificationPanelViewController extends OverlayPanelViewController
|
||||
private final CarServiceProvider mCarServiceProvider;
|
||||
private final IStatusBarService mBarService;
|
||||
private final CommandQueue mCommandQueue;
|
||||
private final Executor mUiBgExecutor;
|
||||
private final NotificationDataManager mNotificationDataManager;
|
||||
private final CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper;
|
||||
private final CarNotificationListener mCarNotificationListener;
|
||||
private final NotificationClickHandlerFactory mNotificationClickHandlerFactory;
|
||||
private final StatusBarStateController mStatusBarStateController;
|
||||
private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
|
||||
private final NotificationVisibilityLogger mNotificationVisibilityLogger;
|
||||
|
||||
private float mInitialBackgroundAlpha;
|
||||
private float mBackgroundAlphaDiff;
|
||||
@@ -98,6 +104,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController
|
||||
@Main Resources resources,
|
||||
OverlayViewGlobalStateController overlayViewGlobalStateController,
|
||||
FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
|
||||
@UiBackground Executor uiBgExecutor,
|
||||
|
||||
/* Other things */
|
||||
CarServiceProvider carServiceProvider,
|
||||
@@ -110,6 +117,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController
|
||||
CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
|
||||
CarNotificationListener carNotificationListener,
|
||||
NotificationClickHandlerFactory notificationClickHandlerFactory,
|
||||
NotificationVisibilityLogger notificationVisibilityLogger,
|
||||
|
||||
/* Things that need to be replaced */
|
||||
StatusBarStateController statusBarStateController
|
||||
@@ -121,12 +129,15 @@ public class NotificationPanelViewController extends OverlayPanelViewController
|
||||
mCarServiceProvider = carServiceProvider;
|
||||
mBarService = barService;
|
||||
mCommandQueue = commandQueue;
|
||||
mUiBgExecutor = uiBgExecutor;
|
||||
mNotificationDataManager = notificationDataManager;
|
||||
mCarUxRestrictionManagerWrapper = carUxRestrictionManagerWrapper;
|
||||
mCarNotificationListener = carNotificationListener;
|
||||
mNotificationClickHandlerFactory = notificationClickHandlerFactory;
|
||||
mStatusBarStateController = statusBarStateController;
|
||||
mNotificationVisibilityLogger = notificationVisibilityLogger;
|
||||
|
||||
mCommandQueue.addCallback(this);
|
||||
// Notification background setup.
|
||||
mInitialBackgroundAlpha = (float) mResources.getInteger(
|
||||
R.integer.config_initialNotificationBackgroundAlpha) / 100;
|
||||
@@ -151,11 +162,35 @@ public class NotificationPanelViewController extends OverlayPanelViewController
|
||||
.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
|
||||
}
|
||||
|
||||
// CommandQueue.Callbacks
|
||||
|
||||
@Override
|
||||
public void animateExpandNotificationsPanel() {
|
||||
if (!isPanelExpanded()) {
|
||||
toggle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void animateCollapsePanels(int flags, boolean force) {
|
||||
if (isPanelExpanded()) {
|
||||
toggle();
|
||||
}
|
||||
}
|
||||
|
||||
// OverlayViewController
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
reinflate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void hideInternal() {
|
||||
super.hideInternal();
|
||||
mNotificationVisibilityLogger.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldShowNavigationBar() {
|
||||
return true;
|
||||
@@ -197,6 +232,11 @@ public class NotificationPanelViewController extends OverlayPanelViewController
|
||||
mUnseenCountUpdateListener.onUnseenCountUpdate(
|
||||
mNotificationDataManager.getUnseenNotificationCount());
|
||||
}
|
||||
mCarNotificationListener.setNotificationsShown(
|
||||
mNotificationDataManager.getSeenNotifications());
|
||||
// This logs both when the notification panel is expanded and when the notification
|
||||
// panel is scrolled.
|
||||
mNotificationVisibilityLogger.log(isPanelExpanded());
|
||||
});
|
||||
|
||||
mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
|
||||
@@ -332,6 +372,8 @@ public class NotificationPanelViewController extends OverlayPanelViewController
|
||||
mNotificationDataManager.clearAll();
|
||||
}
|
||||
|
||||
// OverlayPanelViewController
|
||||
|
||||
@Override
|
||||
protected boolean shouldAnimateCollapsePanel() {
|
||||
return true;
|
||||
@@ -363,6 +405,30 @@ public class NotificationPanelViewController extends OverlayPanelViewController
|
||||
mNotificationView.setVisibleNotificationsAsSeen();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPanelVisible(boolean visible) {
|
||||
super.onPanelVisible(visible);
|
||||
mUiBgExecutor.execute(() -> {
|
||||
try {
|
||||
if (visible) {
|
||||
// When notification panel is open even just a bit, we want to clear
|
||||
// notification effects.
|
||||
boolean clearNotificationEffects =
|
||||
mStatusBarStateController.getState() != StatusBarState.KEYGUARD;
|
||||
mBarService.onPanelRevealed(clearNotificationEffects,
|
||||
mNotificationDataManager.getVisibleNotifications().size());
|
||||
} else {
|
||||
mBarService.onPanelHidden();
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
// Won't fail unless the world has ended.
|
||||
Log.e(TAG, String.format(
|
||||
"Unable to notify StatusBarService of panel visibility: %s", visible));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPanelExpanded(boolean expand) {
|
||||
super.onPanelExpanded(expand);
|
||||
@@ -373,6 +439,9 @@ public class NotificationPanelViewController extends OverlayPanelViewController
|
||||
}
|
||||
clearNotificationEffects();
|
||||
}
|
||||
if (!expand) {
|
||||
mNotificationVisibilityLogger.log(isPanelExpanded());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.car.notification;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.car.notification.AlertEntry;
|
||||
import com.android.car.notification.NotificationDataManager;
|
||||
import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.internal.statusbar.NotificationVisibility;
|
||||
import com.android.systemui.dagger.qualifiers.UiBackground;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Handles notification logging, in particular, logging which notifications are visible and which
|
||||
* are not.
|
||||
*/
|
||||
@Singleton
|
||||
public class NotificationVisibilityLogger {
|
||||
|
||||
private static final String TAG = "NotificationVisibilityLogger";
|
||||
|
||||
private final ArraySet<NotificationVisibility> mCurrentlyVisible = new ArraySet<>();
|
||||
private final ArraySet<NotificationVisibility> mNewlyVisible = new ArraySet<>();
|
||||
private final ArraySet<NotificationVisibility> mPreviouslyVisible = new ArraySet<>();
|
||||
private final ArraySet<NotificationVisibility> mTmpCurrentlyVisible = new ArraySet<>();
|
||||
|
||||
private final IStatusBarService mBarService;
|
||||
private final Executor mUiBgExecutor;
|
||||
private final NotificationDataManager mNotificationDataManager;
|
||||
|
||||
private boolean mIsVisible;
|
||||
|
||||
private final Runnable mVisibilityReporter = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (mIsVisible) {
|
||||
int count = mNotificationDataManager.getVisibleNotifications().size();
|
||||
for (AlertEntry alertEntry : mNotificationDataManager.getVisibleNotifications()) {
|
||||
NotificationVisibility visObj = NotificationVisibility.obtain(
|
||||
alertEntry.getKey(),
|
||||
/* rank= */ -1,
|
||||
count,
|
||||
mIsVisible,
|
||||
NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA);
|
||||
mTmpCurrentlyVisible.add(visObj);
|
||||
if (!mCurrentlyVisible.contains(visObj)) {
|
||||
mNewlyVisible.add(visObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
mPreviouslyVisible.addAll(mCurrentlyVisible);
|
||||
mPreviouslyVisible.removeAll(mTmpCurrentlyVisible);
|
||||
onNotificationVisibilityChanged(mNewlyVisible, mPreviouslyVisible);
|
||||
|
||||
recycleAllVisibilityObjects(mCurrentlyVisible);
|
||||
mCurrentlyVisible.addAll(mTmpCurrentlyVisible);
|
||||
|
||||
recycleAllVisibilityObjects(mPreviouslyVisible);
|
||||
recycleAllVisibilityObjects(mNewlyVisible);
|
||||
recycleAllVisibilityObjects(mTmpCurrentlyVisible);
|
||||
}
|
||||
};
|
||||
|
||||
@Inject
|
||||
public NotificationVisibilityLogger(
|
||||
@UiBackground Executor uiBgExecutor,
|
||||
IStatusBarService barService,
|
||||
NotificationDataManager notificationDataManager) {
|
||||
mUiBgExecutor = uiBgExecutor;
|
||||
mBarService = barService;
|
||||
mNotificationDataManager = notificationDataManager;
|
||||
}
|
||||
|
||||
/** Triggers a visibility report update to be sent to StatusBarService. */
|
||||
public void log(boolean isVisible) {
|
||||
mIsVisible = isVisible;
|
||||
mUiBgExecutor.execute(mVisibilityReporter);
|
||||
}
|
||||
|
||||
/** Stops logging, clearing all visibility objects. */
|
||||
public void stop() {
|
||||
recycleAllVisibilityObjects(mCurrentlyVisible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify StatusBarService of change in notifications' visibility.
|
||||
*/
|
||||
private void onNotificationVisibilityChanged(
|
||||
Set<NotificationVisibility> newlyVisible, Set<NotificationVisibility> noLongerVisible) {
|
||||
if (newlyVisible.isEmpty() && noLongerVisible.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mBarService.onNotificationVisibilityChanged(
|
||||
cloneVisibilitiesAsArr(newlyVisible), cloneVisibilitiesAsArr(noLongerVisible));
|
||||
} catch (RemoteException e) {
|
||||
// Won't fail unless the world has ended.
|
||||
Log.e(TAG, "Failed to notify StatusBarService of notification visibility change");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears array and recycles NotificationVisibility objects for reuse.
|
||||
*/
|
||||
private static void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
array.valueAt(i).recycle();
|
||||
}
|
||||
array.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Set of NotificationVisibility objects to primitive array.
|
||||
*/
|
||||
private static NotificationVisibility[] cloneVisibilitiesAsArr(Set<NotificationVisibility> c) {
|
||||
NotificationVisibility[] array = new NotificationVisibility[c.size()];
|
||||
int i = 0;
|
||||
for (NotificationVisibility nv : c) {
|
||||
if (nv != null) {
|
||||
array[i] = nv.clone();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.car.notification;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.car.notification.AlertEntry;
|
||||
import com.android.car.notification.NotificationDataManager;
|
||||
import com.android.internal.statusbar.IStatusBarService;
|
||||
import com.android.internal.statusbar.NotificationVisibility;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.util.concurrency.FakeExecutor;
|
||||
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.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper
|
||||
@SmallTest
|
||||
public class NotificationVisibilityLoggerTest extends SysuiTestCase {
|
||||
|
||||
private static final String PKG = "package_1";
|
||||
private static final String OP_PKG = "OpPackage";
|
||||
private static final int ID = 1;
|
||||
private static final String TAG = "Tag";
|
||||
private static final int UID = 2;
|
||||
private static final int INITIAL_PID = 3;
|
||||
private static final String CHANNEL_ID = "CHANNEL_ID";
|
||||
private static final String CONTENT_TITLE = "CONTENT_TITLE";
|
||||
private static final String OVERRIDE_GROUP_KEY = "OVERRIDE_GROUP_KEY";
|
||||
private static final long POST_TIME = 12345L;
|
||||
private static final UserHandle USER_HANDLE = new UserHandle(12);
|
||||
|
||||
@Mock
|
||||
private IStatusBarService mBarService;
|
||||
@Mock
|
||||
private NotificationDataManager mNotificationDataManager;
|
||||
|
||||
private NotificationVisibilityLogger mNotificationVisibilityLogger;
|
||||
private FakeExecutor mUiBgExecutor;
|
||||
private AlertEntry mMessageNotification;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(/* testClass= */this);
|
||||
|
||||
mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
|
||||
Notification.Builder mNotificationBuilder1 = new Notification.Builder(mContext, CHANNEL_ID)
|
||||
.setContentTitle(CONTENT_TITLE);
|
||||
mMessageNotification = new AlertEntry(new StatusBarNotification(PKG, OP_PKG,
|
||||
ID, TAG, UID, INITIAL_PID, mNotificationBuilder1.build(), USER_HANDLE,
|
||||
OVERRIDE_GROUP_KEY, POST_TIME));
|
||||
|
||||
when(mNotificationDataManager.getVisibleNotifications()).thenReturn(
|
||||
Collections.singletonList(mMessageNotification));
|
||||
|
||||
mNotificationVisibilityLogger = new NotificationVisibilityLogger(
|
||||
mUiBgExecutor, mBarService, mNotificationDataManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void log_notifiesStatusBarService() throws RemoteException {
|
||||
mNotificationVisibilityLogger.log(/* isVisible= */ true);
|
||||
mUiBgExecutor.runNextReady();
|
||||
|
||||
verify(mBarService).onNotificationVisibilityChanged(
|
||||
any(NotificationVisibility[].class), any(NotificationVisibility[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void log_isVisibleIsTrue_notifiesOfNewlyVisibleItems() throws RemoteException {
|
||||
ArgumentCaptor<NotificationVisibility[]> newlyVisibleCaptor =
|
||||
ArgumentCaptor.forClass(NotificationVisibility[].class);
|
||||
ArgumentCaptor<NotificationVisibility[]> previouslyVisibleCaptor =
|
||||
ArgumentCaptor.forClass(NotificationVisibility[].class);
|
||||
|
||||
mNotificationVisibilityLogger.log(/* isVisible= */ true);
|
||||
mUiBgExecutor.runNextReady();
|
||||
|
||||
verify(mBarService).onNotificationVisibilityChanged(
|
||||
newlyVisibleCaptor.capture(), previouslyVisibleCaptor.capture());
|
||||
assertThat(newlyVisibleCaptor.getValue().length).isEqualTo(1);
|
||||
assertThat(previouslyVisibleCaptor.getValue().length).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void log_isVisibleIsFalse_notifiesOfPreviouslyVisibleItems() throws RemoteException {
|
||||
ArgumentCaptor<NotificationVisibility[]> newlyVisibleCaptor =
|
||||
ArgumentCaptor.forClass(NotificationVisibility[].class);
|
||||
ArgumentCaptor<NotificationVisibility[]> previouslyVisibleCaptor =
|
||||
ArgumentCaptor.forClass(NotificationVisibility[].class);
|
||||
mNotificationVisibilityLogger.log(/* isVisible= */ true);
|
||||
mUiBgExecutor.runNextReady();
|
||||
reset(mBarService);
|
||||
|
||||
mNotificationVisibilityLogger.log(/* isVisible= */ false);
|
||||
mUiBgExecutor.runNextReady();
|
||||
|
||||
verify(mBarService).onNotificationVisibilityChanged(
|
||||
newlyVisibleCaptor.capture(), previouslyVisibleCaptor.capture());
|
||||
assertThat(previouslyVisibleCaptor.getValue().length).isEqualTo(1);
|
||||
assertThat(newlyVisibleCaptor.getValue().length).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user