summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/CarSystemUI/res/layout/notification_center_activity.xml2
-rw-r--r--packages/CarSystemUI/res/layout/notification_panel_container.xml21
-rw-r--r--packages/CarSystemUI/res/layout/sysui_overlay_window.xml5
-rw-r--r--packages/CarSystemUI/res/values/config.xml1
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java4
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java9
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java778
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java126
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarShadeControllerImpl.java85
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java702
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java21
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java6
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/window/OverlayViewController.java5
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java35
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/window/OverlayWindowModule.java10
-rw-r--r--packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java64
-rw-r--r--packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewGlobalStateControllerTest.java26
17 files changed, 1048 insertions, 852 deletions
diff --git a/packages/CarSystemUI/res/layout/notification_center_activity.xml b/packages/CarSystemUI/res/layout/notification_center_activity.xml
index 0af74c4462a6..4fef48918c97 100644
--- a/packages/CarSystemUI/res/layout/notification_center_activity.xml
+++ b/packages/CarSystemUI/res/layout/notification_center_activity.xml
@@ -20,6 +20,8 @@
android:id="@+id/notification_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:visibility="invisible"
+ android:layout_marginBottom="@dimen/navigation_bar_height"
android:background="@color/notification_shade_background_color">
<View
diff --git a/packages/CarSystemUI/res/layout/notification_panel_container.xml b/packages/CarSystemUI/res/layout/notification_panel_container.xml
new file mode 100644
index 000000000000..bf71396984b8
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/notification_panel_container.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index cc36e87eb480..1b0a211b733d 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -22,6 +22,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <ViewStub android:id="@+id/notification_panel_stub"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout="@layout/notification_panel_container"/>
+
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index aaa65de2fa1d..b629e1417f35 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -61,6 +61,7 @@
<!-- Car System UI's OverlayViewsMediator-->
<string-array name="config_carSystemUIOverlayViewsMediators" translatable="false">
<item>com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator</item>
+ <item>com.android.systemui.car.notification.NotificationPanelViewMediator</item>
</string-array>
<!-- SystemUI Services: The classes of the stuff to start. -->
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 14d5bd53a9ab..5547fee0159c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -38,7 +38,6 @@ import com.android.systemui.stackdivider.DividerModule;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
-import com.android.systemui.statusbar.car.CarShadeControllerImpl;
import com.android.systemui.statusbar.car.CarStatusBar;
import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -47,6 +46,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.ShadeControllerImpl;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -120,7 +120,7 @@ abstract class CarSystemUIModule {
KeyguardEnvironmentImpl keyguardEnvironment);
@Binds
- abstract ShadeController provideShadeController(CarShadeControllerImpl shadeController);
+ abstract ShadeController provideShadeController(ShadeControllerImpl shadeController);
@Provides
@Singleton
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java
index c870cec67314..b057198d5177 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java
@@ -29,9 +29,16 @@ public interface CarDeviceProvisionedController extends DeviceProvisionedControl
boolean isUserSetupInProgress(int user);
/**
- * Returns {@code true} then SUW is in progress for the current user.
+ * Returns {@code true} when SUW is in progress for the current user.
*/
default boolean isCurrentUserSetupInProgress() {
return isUserSetupInProgress(getCurrentUser());
}
+
+ /**
+ * Returns {@code true} when the user is setup and not currently in SUW.
+ */
+ default boolean isCurrentUserFullySetup() {
+ return isCurrentUserSetup() && !isCurrentUserSetupInProgress();
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
new file mode 100644
index 000000000000..f6679c0e681f
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -0,0 +1,778 @@
+/*
+ * 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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.car.Car;
+import android.car.drivingstate.CarUxRestrictionsManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.car.notification.CarNotificationListener;
+import com.android.car.notification.CarNotificationView;
+import com.android.car.notification.CarUxRestrictionManagerWrapper;
+import com.android.car.notification.NotificationClickHandlerFactory;
+import com.android.car.notification.NotificationDataManager;
+import com.android.car.notification.NotificationViewController;
+import com.android.car.notification.PreprocessingManager;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.R;
+import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.dagger.qualifiers.Main;
+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 com.android.systemui.window.OverlayViewController;
+import com.android.systemui.window.OverlayViewGlobalStateController;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** View controller for the notification panel. */
+@Singleton
+public class NotificationPanelViewController extends OverlayViewController {
+
+ // used to calculate how fast to open or close the window
+ private static final float DEFAULT_FLING_VELOCITY = 0;
+ // max time a fling animation takes
+ private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
+ // acceleration rate for the fling animation
+ private static final float FLING_SPEED_UP_FACTOR = 0.6f;
+
+ private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
+ private static final int SWIPE_MAX_OFF_PATH = 75;
+ private static final int SWIPE_THRESHOLD_VELOCITY = 200;
+ private static final boolean DEBUG = true;
+ private static final String TAG = "NotificationPanelViewController";
+
+ private final Context mContext;
+ private final Resources mResources;
+ private final CarServiceProvider mCarServiceProvider;
+ private final CarDeviceProvisionedController mCarDeviceProvisionedController;
+ private final IStatusBarService mBarService;
+ private final CommandQueue mCommandQueue;
+ private final NotificationDataManager mNotificationDataManager;
+ private final CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper;
+ private final CarNotificationListener mCarNotificationListener;
+ private final NotificationClickHandlerFactory mNotificationClickHandlerFactory;
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private final StatusBarStateController mStatusBarStateController;
+
+ private final int mSettleClosePercentage;
+
+ private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
+ private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
+
+ private float mInitialBackgroundAlpha;
+ private float mBackgroundAlphaDiff;
+
+ private CarNotificationView mNotificationView;
+ private View mHandleBar;
+ private RecyclerView mNotificationList;
+ private NotificationViewController mNotificationViewController;
+
+ private boolean mIsTracking;
+ private boolean mNotificationListAtBottom;
+ private float mFirstTouchDownOnGlassPane;
+ private boolean mNotificationListAtBottomAtTimeOfTouch;
+ private boolean mIsSwipingVerticallyToClose;
+ private int mPercentageFromBottom;
+ private boolean mIsNotificationAnimating;
+ private boolean mIsNotificationCardSwiping;
+ private boolean mPanelExpanded = false;
+
+ private View.OnTouchListener mTopNavBarNotificationTouchListener;
+ private View.OnTouchListener mNavBarNotificationTouchListener;
+
+ private OnUnseenCountUpdateListener mUnseenCountUpdateListener;
+
+ @Inject
+ public NotificationPanelViewController(
+ Context context,
+ @Main Resources resources,
+ OverlayViewGlobalStateController overlayViewGlobalStateController,
+
+ /* Other things */
+ CarServiceProvider carServiceProvider,
+ CarDeviceProvisionedController carDeviceProvisionedController,
+
+ /* Things needed for notifications */
+ IStatusBarService barService,
+ CommandQueue commandQueue,
+ NotificationDataManager notificationDataManager,
+ CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
+ CarNotificationListener carNotificationListener,
+ NotificationClickHandlerFactory notificationClickHandlerFactory,
+ FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
+
+ /* Things that need to be replaced */
+ StatusBarStateController statusBarStateController
+ ) {
+ super(R.id.notification_panel_stub, overlayViewGlobalStateController);
+ mContext = context;
+ mResources = resources;
+ mCarServiceProvider = carServiceProvider;
+ mCarDeviceProvisionedController = carDeviceProvisionedController;
+ mBarService = barService;
+ mCommandQueue = commandQueue;
+ mNotificationDataManager = notificationDataManager;
+ mCarUxRestrictionManagerWrapper = carUxRestrictionManagerWrapper;
+ mCarNotificationListener = carNotificationListener;
+ mNotificationClickHandlerFactory = notificationClickHandlerFactory;
+ mFlingAnimationUtils = flingAnimationUtilsBuilder
+ .setMaxLengthSeconds(FLING_ANIMATION_MAX_TIME)
+ .setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
+ .build();
+ mStatusBarStateController = statusBarStateController;
+
+ // Notification background setup.
+ mInitialBackgroundAlpha = (float) mResources.getInteger(
+ R.integer.config_initialNotificationBackgroundAlpha) / 100;
+ if (mInitialBackgroundAlpha < 0 || mInitialBackgroundAlpha > 100) {
+ throw new RuntimeException(
+ "Unable to setup notification bar due to incorrect initial background alpha"
+ + " percentage");
+ }
+ float finalBackgroundAlpha = Math.max(
+ mInitialBackgroundAlpha,
+ (float) mResources.getInteger(
+ R.integer.config_finalNotificationBackgroundAlpha) / 100);
+ if (finalBackgroundAlpha < 0 || finalBackgroundAlpha > 100) {
+ throw new RuntimeException(
+ "Unable to setup notification bar due to incorrect final background alpha"
+ + " percentage");
+ }
+ mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha;
+
+ // Notification Panel param setup
+ mSettleClosePercentage = mResources.getInteger(
+ R.integer.notification_settle_close_percentage);
+
+ // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
+ // notification shade.
+ GestureDetector openGestureDetector = new GestureDetector(mContext,
+ new OpenNotificationGestureListener() {
+ @Override
+ protected void openNotification() {
+ animateExpandNotificationsPanel();
+ }
+ });
+
+ // Attached to the NavBars to close the notification shade
+ GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext,
+ new NavBarCloseNotificationGestureListener() {
+ @Override
+ protected void close() {
+ if (mPanelExpanded) {
+ animateCollapsePanels();
+ }
+ }
+ });
+
+ mTopNavBarNotificationTouchListener = (v, event) -> {
+ if (!isInflated()) {
+ getOverlayViewGlobalStateController().inflateView(this);
+ }
+ if (!mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
+ return true;
+ }
+
+ boolean consumed = openGestureDetector.onTouchEvent(event);
+ if (consumed) {
+ return true;
+ }
+ maybeCompleteAnimation(event);
+ return true;
+ };
+
+ mNavBarNotificationTouchListener =
+ (v, event) -> {
+ boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
+ if (consumed) {
+ return true;
+ }
+ maybeCompleteAnimation(event);
+ return true;
+ };
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ reinflate();
+ }
+
+ /** Reinflates the view. */
+ public void reinflate() {
+ ViewGroup container = (ViewGroup) getLayout();
+ container.removeView(mNotificationView);
+
+ mNotificationView = (CarNotificationView) LayoutInflater.from(mContext).inflate(
+ R.layout.notification_center_activity, container,
+ /* attachToRoot= */ false);
+
+ container.addView(mNotificationView);
+ onNotificationViewInflated();
+ }
+
+ private void onNotificationViewInflated() {
+ // Find views.
+ mNotificationView = getLayout().findViewById(R.id.notification_view);
+ View glassPane = mNotificationView.findViewById(R.id.glass_pane);
+ mHandleBar = mNotificationView.findViewById(R.id.handle_bar);
+ mNotificationList = mNotificationView.findViewById(R.id.notifications);
+
+ mNotificationClickHandlerFactory.registerClickListener((launchResult, alertEntry) -> {
+ if (launchResult == ActivityManager.START_TASK_TO_FRONT
+ || launchResult == ActivityManager.START_SUCCESS) {
+ animateCollapsePanels();
+ }
+ });
+
+ mNotificationDataManager.setOnUnseenCountUpdateListener(() -> {
+ if (mUnseenCountUpdateListener != null) {
+ mUnseenCountUpdateListener.onUnseenCountUpdate(
+ mNotificationDataManager.getUnseenNotificationCount());
+ }
+ });
+ mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
+ mNotificationView.setClickHandlerFactory(mNotificationClickHandlerFactory);
+ mNotificationView.setNotificationDataManager(mNotificationDataManager);
+
+ mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
+ super.onScrolled(recyclerView, dx, dy);
+ if (!mNotificationList.canScrollVertically(1)) {
+ mNotificationListAtBottom = true;
+ return;
+ }
+ mNotificationListAtBottom = false;
+ mIsSwipingVerticallyToClose = false;
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+ });
+
+ // Attached to the notification ui to detect close request of the notification shade.
+ GestureDetector closeGestureDetector = new GestureDetector(mContext,
+ new CloseNotificationGestureListener() {
+ @Override
+ protected void close() {
+ if (mPanelExpanded) {
+ animateCollapsePanels();
+ }
+ }
+ });
+
+ // Attached to the Handle bar to close the notification shade
+ GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
+ new HandleBarCloseNotificationGestureListener());
+
+ // The glass pane is used to view touch events before passed to the notification list.
+ // This allows us to initialize gesture listeners and detect when to close the notifications
+ glassPane.setOnTouchListener((v, event) -> {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mFirstTouchDownOnGlassPane = event.getRawX();
+ mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
+ // Reset the tracker when there is a touch down on the glass pane.
+ mIsTracking = false;
+ // Pass the down event to gesture detector so that it knows where the touch event
+ // started.
+ closeGestureDetector.onTouchEvent(event);
+ }
+ return false;
+ });
+
+ mNotificationList.setOnTouchListener((v, event) -> {
+ mIsNotificationCardSwiping = Math.abs(mFirstTouchDownOnGlassPane - event.getRawX())
+ > SWIPE_MAX_OFF_PATH;
+ if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
+ // We need to save the state here as if notification card is swiping we will
+ // change the mNotificationListAtBottomAtTimeOfTouch. This is to protect
+ // closing the notification shade while the notification card is being swiped.
+ mIsSwipingVerticallyToClose = true;
+ }
+
+ // If the card is swiping we should not allow the notification shade to close.
+ // Hence setting mNotificationListAtBottomAtTimeOfTouch to false will stop that
+ // for us. We are also checking for mIsTracking because while swiping the
+ // notification shade to close if the user goes a bit horizontal while swiping
+ // upwards then also this should close.
+ if (mIsNotificationCardSwiping && !mIsTracking) {
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+
+ boolean handled = closeGestureDetector.onTouchEvent(event);
+ boolean isTracking = mIsTracking;
+ Rect rect = mNotificationView.getClipBounds();
+ float clippedHeight = 0;
+ if (rect != null) {
+ clippedHeight = rect.bottom;
+ }
+ if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP
+ && mIsSwipingVerticallyToClose) {
+ if (mSettleClosePercentage < mPercentageFromBottom && isTracking) {
+ animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
+ } else if (clippedHeight != mNotificationView.getHeight() && isTracking) {
+ // this can be caused when user is at the end of the list and trying to
+ // fling to top of the list by scrolling down.
+ animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
+ }
+ }
+
+ // Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after
+ // the event has been passed to the closeGestureDetector above, such that the
+ // closeGestureDetector sees the up event before the state has changed.
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mNotificationListAtBottomAtTimeOfTouch = false;
+ }
+ return handled || isTracking;
+ });
+
+ mCarServiceProvider.addListener(car -> {
+ CarUxRestrictionsManager carUxRestrictionsManager =
+ (CarUxRestrictionsManager)
+ car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
+ mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager(
+ carUxRestrictionsManager);
+
+ mNotificationViewController = new NotificationViewController(
+ mNotificationView,
+ PreprocessingManager.getInstance(mContext),
+ mCarNotificationListener,
+ mCarUxRestrictionManagerWrapper,
+ mNotificationDataManager);
+ mNotificationViewController.enable();
+ });
+
+ mHandleBar.setOnTouchListener((v, event) -> {
+ handleBarCloseNotificationGestureDetector.onTouchEvent(event);
+ maybeCompleteAnimation(event);
+ return true;
+ });
+ }
+
+ /** Called when the car power state is changed to ON. */
+ public void onCarPowerStateOn() {
+ if (mNotificationClickHandlerFactory != null) {
+ mNotificationClickHandlerFactory.clearAllNotifications();
+ }
+ mNotificationDataManager.clearAll();
+ }
+
+ View.OnTouchListener getTopNavBarNotificationTouchListener() {
+ return mTopNavBarNotificationTouchListener;
+ }
+
+ View.OnTouchListener getNavBarNotificationTouchListener() {
+ return mNavBarNotificationTouchListener;
+ }
+
+ private void maybeCompleteAnimation(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP
+ && mNotificationView.getVisibility() == View.VISIBLE) {
+ if (mSettleClosePercentage < mPercentageFromBottom) {
+ animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
+ } else {
+ animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
+ }
+ }
+ }
+
+ /**
+ * Animates the notification shade from one position to other. This is used to either open or
+ * close the notification shade completely with a velocity. If the animation is to close the
+ * notification shade this method also makes the view invisible after animation ends.
+ */
+ private void animateNotificationPanel(float velocity, boolean isClosing) {
+ float to = 0;
+ if (!isClosing) {
+ to = mNotificationView.getHeight();
+ }
+
+ Rect rect = mNotificationView.getClipBounds();
+ if (rect != null && rect.bottom != to) {
+ float from = rect.bottom;
+ animate(from, to, velocity, isClosing);
+ return;
+ }
+
+ // We will only be here if the shade is being opened programmatically or via button when
+ // height of the layout was not calculated.
+ ViewTreeObserver notificationTreeObserver = mNotificationView.getViewTreeObserver();
+ notificationTreeObserver.addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ ViewTreeObserver obs = mNotificationView.getViewTreeObserver();
+ obs.removeOnGlobalLayoutListener(this);
+ float to = mNotificationView.getHeight();
+ animate(/* from= */ 0, to, velocity, isClosing);
+ }
+ });
+ }
+
+ private void animateCollapsePanels() {
+ if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) {
+ return;
+ }
+ getOverlayViewGlobalStateController().setWindowFocusable(false);
+ animateNotificationPanel(mClosingVelocity, true);
+ }
+
+ private void animateExpandNotificationsPanel() {
+ if (!mCommandQueue.panelsEnabled()
+ || !mCarDeviceProvisionedController.isCurrentUserFullySetup()) {
+ return;
+ }
+ // scroll to top
+ mNotificationList.scrollToPosition(0);
+ setPanelVisible(true);
+ mNotificationView.setVisibility(View.VISIBLE);
+ animateNotificationPanel(mOpeningVelocity, false);
+
+ setPanelExpanded(true);
+ }
+
+ private void animate(float from, float to, float velocity, boolean isClosing) {
+ if (mIsNotificationAnimating) {
+ return;
+ }
+ mIsNotificationAnimating = true;
+ mIsTracking = true;
+ ValueAnimator animator = ValueAnimator.ofFloat(from, to);
+ animator.addUpdateListener(
+ animation -> {
+ float animatedValue = (Float) animation.getAnimatedValue();
+ setNotificationViewClipBounds((int) animatedValue);
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mIsNotificationAnimating = false;
+ mIsTracking = false;
+ mOpeningVelocity = DEFAULT_FLING_VELOCITY;
+ mClosingVelocity = DEFAULT_FLING_VELOCITY;
+ if (isClosing) {
+ setPanelVisible(false);
+ mNotificationView.setVisibility(View.INVISIBLE);
+ mNotificationView.setClipBounds(null);
+ mNotificationViewController.onVisibilityChanged(false);
+ // let the status bar know that the panel is closed
+ setPanelExpanded(false);
+ } else {
+ mNotificationViewController.onVisibilityChanged(true);
+ // let the status bar know that the panel is open
+ mNotificationView.setVisibleNotificationsAsSeen();
+ setPanelExpanded(true);
+ }
+ }
+ });
+ mFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity));
+ animator.start();
+ }
+
+ /**
+ * Set the panel view to be visible.
+ */
+ public void setPanelVisible(boolean visible) {
+ if (visible && !getOverlayViewGlobalStateController().isWindowVisible()) {
+ getOverlayViewGlobalStateController().setWindowVisible(true);
+ }
+ if (!visible && getOverlayViewGlobalStateController().isWindowVisible()) {
+ getOverlayViewGlobalStateController().setWindowVisible(false);
+ }
+ getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ getOverlayViewGlobalStateController().setWindowFocusable(visible);
+ }
+
+ /**
+ * Set the panel state to expanded. This will expand or collapse the overlay window if
+ * necessary.
+ */
+ public void setPanelExpanded(boolean expand) {
+ mPanelExpanded = expand;
+
+ if (expand && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
+ if (DEBUG) {
+ Log.v(TAG, "clearing notification effects from setExpandedHeight");
+ }
+ clearNotificationEffects();
+ }
+ }
+
+ /**
+ * Clear Buzz/Beep/Blink.
+ */
+ private void clearNotificationEffects() {
+ try {
+ mBarService.clearNotificationEffects();
+ } catch (RemoteException e) {
+ // Won't fail unless the world has ended.
+ }
+ }
+
+ private void setNotificationViewClipBounds(int height) {
+ if (height > mNotificationView.getHeight()) {
+ height = mNotificationView.getHeight();
+ }
+ Rect clipBounds = new Rect();
+ clipBounds.set(0, 0, mNotificationView.getWidth(), height);
+ // Sets the clip region on the notification list view.
+ mNotificationView.setClipBounds(clipBounds);
+ if (mHandleBar != null) {
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) mHandleBar.getLayoutParams();
+ mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin);
+ }
+ if (mNotificationView.getHeight() > 0) {
+ Drawable background = mNotificationView.getBackground().mutate();
+ background.setAlpha((int) (getBackgroundAlpha(height) * 255));
+ mNotificationView.setBackground(background);
+ }
+ }
+
+ /**
+ * Calculates the alpha value for the background based on how much of the notification
+ * shade is visible to the user. When the notification shade is completely open then
+ * alpha value will be 1.
+ */
+ private float getBackgroundAlpha(int height) {
+ return mInitialBackgroundAlpha
+ + ((float) height / mNotificationView.getHeight() * mBackgroundAlphaDiff);
+ }
+
+ private void calculatePercentageFromBottom(float height) {
+ if (mNotificationView.getHeight() > 0) {
+ mPercentageFromBottom = (int) Math.abs(
+ height / mNotificationView.getHeight() * 100);
+ }
+ }
+
+ /** Toggles the visibility of the notification panel. */
+ public void toggle() {
+ if (!isInflated()) {
+ getOverlayViewGlobalStateController().inflateView(this);
+ }
+ if (mPanelExpanded) {
+ animateCollapsePanels();
+ } else {
+ animateExpandNotificationsPanel();
+ }
+ }
+
+ /** Sets the unseen count listener. */
+ public void setOnUnseenCountUpdateListener(OnUnseenCountUpdateListener listener) {
+ mUnseenCountUpdateListener = listener;
+ }
+
+ /** Listener that is updated when the number of unseen notifications changes. */
+ public interface OnUnseenCountUpdateListener {
+ /**
+ * This method is automatically called whenever there is an update to the number of unseen
+ * notifications. This method can be extended by OEMs to customize the desired logic.
+ */
+ void onUnseenCountUpdate(int unseenNotificationCount);
+ }
+
+ /**
+ * Only responsible for open hooks. Since once the panel opens it covers all elements
+ * there is no need to merge with close.
+ */
+ private abstract class OpenNotificationGestureListener extends
+ GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+
+ if (mNotificationView.getVisibility() == View.INVISIBLE) {
+ // when the on-scroll is called for the first time to open.
+ mNotificationList.scrollToPosition(0);
+ }
+ setPanelVisible(true);
+ mNotificationView.setVisibility(View.VISIBLE);
+
+ // clips the view for the notification shade when the user scrolls to open.
+ setNotificationViewClipBounds((int) event2.getRawY());
+
+ // Initially the scroll starts with height being zero. This checks protects from divide
+ // by zero error.
+ calculatePercentageFromBottom(event2.getRawY());
+
+ mIsTracking = true;
+ return true;
+ }
+
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ if (velocityY > SWIPE_THRESHOLD_VELOCITY) {
+ mOpeningVelocity = velocityY;
+ openNotification();
+ return true;
+ }
+ animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
+
+ return false;
+ }
+
+ protected abstract void openNotification();
+ }
+
+ /**
+ * To be installed on the open panel notification panel
+ */
+ private abstract class CloseNotificationGestureListener extends
+ GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent motionEvent) {
+ if (mPanelExpanded) {
+ animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+ // should not clip while scroll to the bottom of the list.
+ if (!mNotificationListAtBottomAtTimeOfTouch) {
+ return false;
+ }
+ float actualNotificationHeight =
+ mNotificationView.getHeight() - (event1.getRawY() - event2.getRawY());
+ if (actualNotificationHeight > mNotificationView.getHeight()) {
+ actualNotificationHeight = mNotificationView.getHeight();
+ }
+ if (mNotificationView.getHeight() > 0) {
+ mPercentageFromBottom = (int) Math.abs(
+ actualNotificationHeight / mNotificationView.getHeight() * 100);
+ boolean isUp = distanceY > 0;
+
+ // This check is to figure out if onScroll was called while swiping the card at
+ // bottom of the list. At that time we should not allow notification shade to
+ // close. We are also checking for the upwards swipe gesture here because it is
+ // possible if a user is closing the notification shade and while swiping starts
+ // to open again but does not fling. At that time we should allow the
+ // notification shade to close fully or else it would stuck in between.
+ if (Math.abs(mNotificationView.getHeight() - actualNotificationHeight)
+ > SWIPE_DOWN_MIN_DISTANCE && isUp) {
+ setNotificationViewClipBounds((int) actualNotificationHeight);
+ mIsTracking = true;
+ } else if (!isUp) {
+ setNotificationViewClipBounds((int) actualNotificationHeight);
+ }
+ }
+ // if we return true the items in RV won't be scrollable.
+ return false;
+ }
+
+
+ @Override
+ public boolean onFling(MotionEvent event1, MotionEvent event2,
+ float velocityX, float velocityY) {
+ // should not fling if the touch does not start when view is at the bottom of the list.
+ if (!mNotificationListAtBottomAtTimeOfTouch) {
+ return false;
+ }
+ if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
+ || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
+ // swipe was not vertical or was not fast enough
+ return false;
+ }
+ boolean isUp = velocityY < 0;
+ if (isUp) {
+ close();
+ return true;
+ } else {
+ // we should close the shade
+ animateNotificationPanel(velocityY, false);
+ }
+ return false;
+ }
+
+ protected abstract void close();
+ }
+
+ /**
+ * To be installed on the nav bars.
+ */
+ private abstract class NavBarCloseNotificationGestureListener extends
+ CloseNotificationGestureListener {
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ mClosingVelocity = DEFAULT_FLING_VELOCITY;
+ if (mPanelExpanded) {
+ close();
+ }
+ return super.onSingleTapUp(e);
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+ calculatePercentageFromBottom(event2.getRawY());
+ setNotificationViewClipBounds((int) event2.getRawY());
+ return true;
+ }
+ }
+
+ /**
+ * To be installed on the handle bar.
+ */
+ private class HandleBarCloseNotificationGestureListener extends
+ GestureDetector.SimpleOnGestureListener {
+
+ @Override
+ public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
+ float distanceY) {
+ calculatePercentageFromBottom(event2.getRawY());
+ // To prevent the jump in the clip bounds while closing the notification shade using
+ // the handle bar we should calculate the height using the diff of event1 and event2.
+ // This will help the notification shade to clip smoothly as the event2 value changes
+ // as event1 value will be fixed.
+ int clipHeight =
+ mNotificationView.getHeight() - (int) (event1.getRawY() - event2.getRawY());
+ setNotificationViewClipBounds(clipHeight);
+ return true;
+ }
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
new file mode 100644
index 000000000000..1cfc83293acb
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java
@@ -0,0 +1,126 @@
+/*
+ * 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.car.hardware.power.CarPowerManager;
+import android.content.res.Configuration;
+
+import com.android.systemui.car.CarDeviceProvisionedController;
+import com.android.systemui.car.CarServiceProvider;
+import com.android.systemui.navigationbar.car.CarNavigationBarController;
+import com.android.systemui.statusbar.car.PowerManagerHelper;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.window.OverlayViewMediator;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/** The view mediator which attaches the view controller to other elements of the system ui. */
+@Singleton
+public class NotificationPanelViewMediator implements OverlayViewMediator,
+ ConfigurationController.ConfigurationListener {
+
+ private final CarNavigationBarController mCarNavigationBarController;
+ private final NotificationPanelViewController mNotificationPanelViewController;
+ private final CarServiceProvider mCarServiceProvider;
+ private final PowerManagerHelper mPowerManagerHelper;
+ private final CarDeviceProvisionedController mCarDeviceProvisionedController;
+ private final ConfigurationController mConfigurationController;
+
+ @Inject
+ public NotificationPanelViewMediator(
+ CarNavigationBarController carNavigationBarController,
+ NotificationPanelViewController notificationPanelViewController,
+
+ CarServiceProvider carServiceProvider,
+ PowerManagerHelper powerManagerHelper,
+
+ CarDeviceProvisionedController carDeviceProvisionedController,
+ ConfigurationController configurationController
+ ) {
+ mCarNavigationBarController = carNavigationBarController;
+ mNotificationPanelViewController = notificationPanelViewController;
+ mCarServiceProvider = carServiceProvider;
+ mPowerManagerHelper = powerManagerHelper;
+ mCarDeviceProvisionedController = carDeviceProvisionedController;
+ mConfigurationController = configurationController;
+ }
+
+ @Override
+ public void registerListeners() {
+ mCarNavigationBarController.registerTopBarTouchListener(
+ mNotificationPanelViewController.getTopNavBarNotificationTouchListener());
+ mCarNavigationBarController.registerBottomBarTouchListener(
+ mNotificationPanelViewController.getNavBarNotificationTouchListener());
+ mCarNavigationBarController.registerLeftBarTouchListener(
+ mNotificationPanelViewController.getNavBarNotificationTouchListener());
+ mCarNavigationBarController.registerRightBarTouchListener(
+ mNotificationPanelViewController.getNavBarNotificationTouchListener());
+
+ mCarNavigationBarController.registerNotificationController(
+ () -> mNotificationPanelViewController.toggle());
+ }
+
+ @Override
+ public void setupOverlayContentViewControllers() {
+ mNotificationPanelViewController.setOnUnseenCountUpdateListener(unseenNotificationCount -> {
+ boolean hasUnseen = unseenNotificationCount > 0;
+ mCarNavigationBarController.toggleAllNotificationsUnseenIndicator(
+ mCarDeviceProvisionedController.isCurrentUserFullySetup(), hasUnseen);
+ });
+
+ mPowerManagerHelper.setCarPowerStateListener(state -> {
+ if (state == CarPowerManager.CarPowerStateListener.ON) {
+ mNotificationPanelViewController.onCarPowerStateOn();
+ }
+ });
+ mPowerManagerHelper.connectToCarService();
+
+ mConfigurationController.addCallback(this);
+ }
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ // No op.
+ }
+
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ registerListeners();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ // No op.
+ }
+
+ @Override
+ public void onUiModeChanged() {
+ // No op.
+ }
+
+ @Override
+ public void onThemeChanged() {
+ // No op.
+ }
+
+ @Override
+ public void onLocaleListChanged() {
+ mNotificationPanelViewController.reinflate();
+ registerListeners();
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarShadeControllerImpl.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarShadeControllerImpl.java
deleted file mode 100644
index 755ed25d64e2..000000000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarShadeControllerImpl.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2019 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.statusbar.car;
-
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.car.notification.CarNotificationView;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.ShadeControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-import dagger.Lazy;
-
-/** Car specific implementation of {@link com.android.systemui.statusbar.phone.ShadeController}. */
-@Singleton
-public class CarShadeControllerImpl extends ShadeControllerImpl {
-
- @Inject
- public CarShadeControllerImpl(CommandQueue commandQueue,
- StatusBarStateController statusBarStateController,
- NotificationShadeWindowController notificationShadeWindowController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- WindowManager windowManager,
- Lazy<StatusBar> statusBarLazy,
- Lazy<AssistManager> assistManagerLazy,
- Lazy<BubbleController> bubbleControllerLazy) {
- super(commandQueue, statusBarStateController, notificationShadeWindowController,
- statusBarKeyguardViewManager, windowManager,
- statusBarLazy, assistManagerLazy, bubbleControllerLazy);
- }
-
- @Override
- public void animateCollapsePanels(int flags, boolean force, boolean delayed,
- float speedUpFactor) {
- super.animateCollapsePanels(flags, force, delayed, speedUpFactor);
- if (!getCarStatusBar().isPanelExpanded()
- || getCarNotificationView().getVisibility() == View.INVISIBLE) {
- return;
- }
-
- mNotificationShadeWindowController.setNotificationShadeFocusable(false);
- getCarStatusBar().getNotificationShadeWindowViewController().cancelExpandHelper();
- getStatusBarView().collapsePanel(true /* animate */, delayed, speedUpFactor);
-
- getCarStatusBar().animateNotificationPanel(getCarStatusBar().getClosingVelocity(), true);
-
- if (!getCarStatusBar().isTracking()) {
- mNotificationShadeWindowController.setPanelVisible(false);
- getCarNotificationView().setVisibility(View.INVISIBLE);
- }
-
- getCarStatusBar().setPanelExpanded(false);
- }
-
- private CarStatusBar getCarStatusBar() {
- return (CarStatusBar) mStatusBarLazy.get();
- }
-
- private CarNotificationView getCarNotificationView() {
- return getCarStatusBar().getCarNotificationView();
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 411f14d1d1ed..02604d870986 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -18,38 +18,15 @@ package com.android.systemui.statusbar.car;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.car.Car;
-import android.car.drivingstate.CarUxRestrictionsManager;
-import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.PowerManager;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.car.notification.CarNotificationListener;
-import com.android.car.notification.CarNotificationView;
-import com.android.car.notification.CarUxRestrictionManagerWrapper;
-import com.android.car.notification.NotificationClickHandlerFactory;
-import com.android.car.notification.NotificationDataManager;
-import com.android.car.notification.NotificationViewController;
-import com.android.car.notification.PreprocessingManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -64,7 +41,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.car.CarDeviceProvisionedController;
import com.android.systemui.car.CarDeviceProvisionedListener;
-import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -84,7 +60,6 @@ import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -156,22 +131,9 @@ import dagger.Lazy;
*/
public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
private static final String TAG = "CarStatusBar";
- // used to calculate how fast to open or close the window
- private static final float DEFAULT_FLING_VELOCITY = 0;
- // max time a fling animation takes
- private static final float FLING_ANIMATION_MAX_TIME = 0.5f;
- // acceleration rate for the fling animation
- private static final float FLING_SPEED_UP_FACTOR = 0.6f;
private final UserSwitcherController mUserSwitcherController;
private final ScrimController mScrimController;
- private final LockscreenLockIconController mLockscreenLockIconController;
-
- private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
- private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
-
- private float mBackgroundAlphaDiff;
- private float mInitialBackgroundAlpha;
private CarBatteryController mCarBatteryController;
private BatteryMeterView mBatteryMeterView;
@@ -179,58 +141,11 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
private final Object mQueueLock = new Object();
private final CarNavigationBarController mCarNavigationBarController;
- private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
- private final Lazy<PowerManagerHelper> mPowerManagerHelperLazy;
- private final ShadeController mShadeController;
- private final CarServiceProvider mCarServiceProvider;
- private final NotificationDataManager mNotificationDataManager;
private final CarDeviceProvisionedController mCarDeviceProvisionedController;
private final ScreenLifecycle mScreenLifecycle;
- private final CarNotificationListener mCarNotificationListener;
private boolean mDeviceIsSetUpForUser = true;
private boolean mIsUserSetupInProgress = false;
- private PowerManagerHelper mPowerManagerHelper;
- private FlingAnimationUtils mFlingAnimationUtils;
- private NotificationClickHandlerFactory mNotificationClickHandlerFactory;
-
- // The container for the notifications.
- private CarNotificationView mNotificationView;
- private RecyclerView mNotificationList;
- // The handler bar view at the bottom of notification shade.
- private View mHandleBar;
- // The controller for the notification view.
- private NotificationViewController mNotificationViewController;
- // The state of if the notification list is currently showing the bottom.
- private boolean mNotificationListAtBottom;
- // Was the notification list at the bottom when the user first touched the screen
- private boolean mNotificationListAtBottomAtTimeOfTouch;
- // To be attached to the top navigation bar (i.e. status bar) to pull down the notification
- // panel.
- private View.OnTouchListener mTopNavBarNotificationTouchListener;
- // To be attached to the navigation bars such that they can close the notification panel if
- // it's open.
- private View.OnTouchListener mNavBarNotificationTouchListener;
- // Percentage from top of the screen after which the notification shade will open. This value
- // will be used while opening the notification shade.
- private int mSettleOpenPercentage;
- // Percentage from top of the screen below which the notification shade will close. This
- // value will be used while closing the notification shade.
- private int mSettleClosePercentage;
- // Percentage of notification shade open from top of the screen.
- private int mPercentageFromBottom;
- // If notification shade is animation to close or to open.
- private boolean mIsNotificationAnimating;
- // Tracks when the notification shade is being scrolled. This refers to the glass pane being
- // scrolled not the recycler view.
- private boolean mIsTracking;
- private float mFirstTouchDownOnGlassPane;
- // If the notification card inside the recycler view is being swiped.
- private boolean mIsNotificationCardSwiping;
- // If notification shade is being swiped vertically to close.
- private boolean mIsSwipingVerticallyToClose;
-
- private final CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper;
public CarStatusBar(
Context context,
@@ -311,13 +226,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
/* Car Settings injected components. */
- CarServiceProvider carServiceProvider,
- Lazy<PowerManagerHelper> powerManagerHelperLazy,
- CarNavigationBarController carNavigationBarController,
- FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
- NotificationDataManager notificationDataManager,
- CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
- CarNotificationListener carNotificationListener) {
+ CarNavigationBarController carNavigationBarController) {
super(
context,
notificationsController,
@@ -398,17 +307,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
statusBarTouchableRegionManager);
mUserSwitcherController = userSwitcherController;
mScrimController = scrimController;
- mLockscreenLockIconController = lockscreenLockIconController;
mCarDeviceProvisionedController = carDeviceProvisionedController;
- mShadeController = shadeController;
- mCarServiceProvider = carServiceProvider;
- mPowerManagerHelperLazy = powerManagerHelperLazy;
mCarNavigationBarController = carNavigationBarController;
- mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
mScreenLifecycle = screenLifecycle;
- mNotificationDataManager = notificationDataManager;
- mCarUxRestrictionManagerWrapper = carUxRestrictionManagerWrapper;
- mCarNotificationListener = carNotificationListener;
}
@Override
@@ -416,54 +317,11 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
mDeviceIsSetUpForUser = mCarDeviceProvisionedController.isCurrentUserSetup();
mIsUserSetupInProgress = mCarDeviceProvisionedController.isCurrentUserSetupInProgress();
- // Notification bar related setup.
- mInitialBackgroundAlpha = (float) mContext.getResources().getInteger(
- R.integer.config_initialNotificationBackgroundAlpha) / 100;
- if (mInitialBackgroundAlpha < 0 || mInitialBackgroundAlpha > 100) {
- throw new RuntimeException(
- "Unable to setup notification bar due to incorrect initial background alpha"
- + " percentage");
- }
- float finalBackgroundAlpha = Math.max(
- mInitialBackgroundAlpha,
- (float) mContext.getResources().getInteger(
- R.integer.config_finalNotificationBackgroundAlpha) / 100);
- if (finalBackgroundAlpha < 0 || finalBackgroundAlpha > 100) {
- throw new RuntimeException(
- "Unable to setup notification bar due to incorrect final background alpha"
- + " percentage");
- }
- mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha;
-
super.start();
- mNotificationPanelViewController.setScrollingEnabled(true);
- mSettleOpenPercentage = mContext.getResources().getInteger(
- R.integer.notification_settle_open_percentage);
- mSettleClosePercentage = mContext.getResources().getInteger(
- R.integer.notification_settle_close_percentage);
- mFlingAnimationUtils = mFlingAnimationUtilsBuilder
- .setMaxLengthSeconds(FLING_ANIMATION_MAX_TIME)
- .setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
- .build();
-
createBatteryController();
mCarBatteryController.startListening();
- mPowerManagerHelper = mPowerManagerHelperLazy.get();
- mPowerManagerHelper.setCarPowerStateListener(
- state -> {
- // When the car powers on, clear all notifications and mute/unread states.
- Log.d(TAG, "New car power state: " + state);
- if (state == CarPowerStateListener.ON) {
- if (mNotificationClickHandlerFactory != null) {
- mNotificationClickHandlerFactory.clearAllNotifications();
- }
- mNotificationDataManager.clearAll();
- }
- });
- mPowerManagerHelper.connectToCarService();
-
mCarDeviceProvisionedController.addCallback(
new CarDeviceProvisionedListener() {
@Override
@@ -540,318 +398,11 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
// when a device has connected by bluetooth.
mBatteryMeterView.setVisibility(View.GONE);
});
-
- connectNotificationsUI();
- }
-
- /**
- * Attach the notification listeners and controllers to the UI as well as build all the
- * touch listeners needed for opening and closing the notification panel
- */
- private void connectNotificationsUI() {
- // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
- // notification shade.
- GestureDetector openGestureDetector = new GestureDetector(mContext,
- new OpenNotificationGestureListener() {
- @Override
- protected void openNotification() {
- animateExpandNotificationsPanel();
- }
- });
- // Attached to the notification ui to detect close request of the notification shade.
- GestureDetector closeGestureDetector = new GestureDetector(mContext,
- new CloseNotificationGestureListener() {
- @Override
- protected void close() {
- if (mPanelExpanded) {
- mShadeController.animateCollapsePanels();
- }
- }
- });
- // Attached to the NavBars to close the notification shade
- GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext,
- new NavBarCloseNotificationGestureListener() {
- @Override
- protected void close() {
- if (mPanelExpanded) {
- mShadeController.animateCollapsePanels();
- }
- }
- });
-
- // Attached to the Handle bar to close the notification shade
- GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
- new HandleBarCloseNotificationGestureListener());
-
- mTopNavBarNotificationTouchListener = (v, event) -> {
- if (!isDeviceSetupForUser()) {
- return true;
- }
- boolean consumed = openGestureDetector.onTouchEvent(event);
- if (consumed) {
- return true;
- }
- maybeCompleteAnimation(event);
- return true;
- };
-
- mNavBarNotificationTouchListener =
- (v, event) -> {
- boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
- if (consumed) {
- return true;
- }
- maybeCompleteAnimation(event);
- return true;
- };
-
- mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(mBarService);
- mNotificationClickHandlerFactory.registerClickListener((launchResult, alertEntry) -> {
- if (launchResult == ActivityManager.START_TASK_TO_FRONT
- || launchResult == ActivityManager.START_SUCCESS) {
- mShadeController.animateCollapsePanels();
- }
- });
-
- mNotificationDataManager.setOnUnseenCountUpdateListener(() -> {
- onUseenCountUpdate(mNotificationDataManager.getUnseenNotificationCount());
- });
-
- mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);
-
- final View glassPane = mNotificationShadeWindowView.findViewById(R.id.glass_pane);
- mNotificationView = mNotificationShadeWindowView.findViewById(R.id.notification_view);
- mHandleBar = mNotificationShadeWindowView.findViewById(R.id.handle_bar);
- mNotificationView.setClickHandlerFactory(mNotificationClickHandlerFactory);
- mNotificationView.setNotificationDataManager(mNotificationDataManager);
-
- // The glass pane is used to view touch events before passed to the notification list.
- // This allows us to initialize gesture listeners and detect when to close the notifications
- glassPane.setOnTouchListener((v, event) -> {
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mFirstTouchDownOnGlassPane = event.getRawX();
- mNotificationListAtBottomAtTimeOfTouch = mNotificationListAtBottom;
- // Reset the tracker when there is a touch down on the glass pane.
- mIsTracking = false;
- // Pass the down event to gesture detector so that it knows where the touch event
- // started.
- closeGestureDetector.onTouchEvent(event);
- }
- return false;
- });
-
- mHandleBar.setOnTouchListener((v, event) -> {
- handleBarCloseNotificationGestureDetector.onTouchEvent(event);
- maybeCompleteAnimation(event);
- return true;
- });
-
- mNotificationList = mNotificationView.findViewById(R.id.notifications);
- mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
- super.onScrolled(recyclerView, dx, dy);
- if (!mNotificationList.canScrollVertically(1)) {
- mNotificationListAtBottom = true;
- return;
- }
- mNotificationListAtBottom = false;
- mIsSwipingVerticallyToClose = false;
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
- });
- mNotificationList.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- mIsNotificationCardSwiping = Math.abs(mFirstTouchDownOnGlassPane - event.getRawX())
- > SWIPE_MAX_OFF_PATH;
- if (mNotificationListAtBottomAtTimeOfTouch && mNotificationListAtBottom) {
- // We need to save the state here as if notification card is swiping we will
- // change the mNotificationListAtBottomAtTimeOfTouch. This is to protect
- // closing the notification shade while the notification card is being swiped.
- mIsSwipingVerticallyToClose = true;
- }
-
- // If the card is swiping we should not allow the notification shade to close.
- // Hence setting mNotificationListAtBottomAtTimeOfTouch to false will stop that
- // for us. We are also checking for mIsTracking because while swiping the
- // notification shade to close if the user goes a bit horizontal while swiping
- // upwards then also this should close.
- if (mIsNotificationCardSwiping && !mIsTracking) {
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
-
- boolean handled = closeGestureDetector.onTouchEvent(event);
- boolean isTracking = mIsTracking;
- Rect rect = mNotificationView.getClipBounds();
- float clippedHeight = 0;
- if (rect != null) {
- clippedHeight = rect.bottom;
- }
- if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP
- && mIsSwipingVerticallyToClose) {
- if (mSettleClosePercentage < mPercentageFromBottom && isTracking) {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
- } else if (clippedHeight != mNotificationView.getHeight() && isTracking) {
- // this can be caused when user is at the end of the list and trying to
- // fling to top of the list by scrolling down.
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
- }
- }
-
- // Updating the mNotificationListAtBottomAtTimeOfTouch state has to be done after
- // the event has been passed to the closeGestureDetector above, such that the
- // closeGestureDetector sees the up event before the state has changed.
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- mNotificationListAtBottomAtTimeOfTouch = false;
- }
- return handled || isTracking;
- }
- });
- mCarServiceProvider.addListener(car -> {
- CarUxRestrictionsManager carUxRestrictionsManager =
- (CarUxRestrictionsManager)
- car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
- mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager(
- carUxRestrictionsManager);
-
- mNotificationViewController = new NotificationViewController(
- mNotificationView,
- PreprocessingManager.getInstance(mContext),
- mCarNotificationListener,
- mCarUxRestrictionManagerWrapper,
- mNotificationDataManager);
- mNotificationViewController.enable();
- });
- }
-
- /**
- * This method is automatically called whenever there is an update to the number of unseen
- * notifications. This method can be extended by OEMs to customize the desired logic.
- */
- protected void onUseenCountUpdate(int unseenNotificationCount) {
- boolean hasUnseen = unseenNotificationCount > 0;
- mCarNavigationBarController.toggleAllNotificationsUnseenIndicator(isDeviceSetupForUser(),
- hasUnseen);
- }
-
- /**
- * @return true if the notification panel is currently visible
- */
- boolean isNotificationPanelOpen() {
- return mPanelExpanded;
}
@Override
public void animateExpandNotificationsPanel() {
- if (!mCommandQueue.panelsEnabled() || !mUserSetup) {
- return;
- }
- // scroll to top
- mNotificationList.scrollToPosition(0);
- mNotificationShadeWindowController.setPanelVisible(true);
- mNotificationView.setVisibility(View.VISIBLE);
- animateNotificationPanel(mOpeningVelocity, false);
-
- setPanelExpanded(true);
- }
-
- public CarNotificationView getCarNotificationView() {
- return mNotificationView;
- }
-
- public float getClosingVelocity() {
- return mClosingVelocity;
- }
-
- public boolean isTracking() {
- return mIsTracking;
- }
-
- private void maybeCompleteAnimation(MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_UP
- && mNotificationView.getVisibility() == View.VISIBLE) {
- if (mSettleClosePercentage < mPercentageFromBottom) {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, false);
- } else {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
- }
- }
- }
-
- /**
- * Animates the notification shade from one position to other. This is used to either open or
- * close the notification shade completely with a velocity. If the animation is to close the
- * notification shade this method also makes the view invisible after animation ends.
- */
- void animateNotificationPanel(float velocity, boolean isClosing) {
- float to = 0;
- if (!isClosing) {
- to = mNotificationView.getHeight();
- }
-
- Rect rect = mNotificationView.getClipBounds();
- if (rect != null && rect.bottom != to) {
- float from = rect.bottom;
- animate(from, to, velocity, isClosing);
- return;
- }
-
- // We will only be here if the shade is being opened programmatically or via button when
- // height of the layout was not calculated.
- ViewTreeObserver notificationTreeObserver = mNotificationView.getViewTreeObserver();
- notificationTreeObserver.addOnGlobalLayoutListener(
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- ViewTreeObserver obs = mNotificationView.getViewTreeObserver();
- obs.removeOnGlobalLayoutListener(this);
- float to = mNotificationView.getHeight();
- animate(/* from= */ 0, to, velocity, isClosing);
- }
- });
- }
-
- private void animate(float from, float to, float velocity, boolean isClosing) {
- if (mIsNotificationAnimating) {
- return;
- }
- mIsNotificationAnimating = true;
- mIsTracking = true;
- ValueAnimator animator = ValueAnimator.ofFloat(from, to);
- animator.addUpdateListener(
- animation -> {
- float animatedValue = (Float) animation.getAnimatedValue();
- setNotificationViewClipBounds((int) animatedValue);
- });
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- mIsNotificationAnimating = false;
- mIsTracking = false;
- mOpeningVelocity = DEFAULT_FLING_VELOCITY;
- mClosingVelocity = DEFAULT_FLING_VELOCITY;
- if (isClosing) {
- mNotificationShadeWindowController.setPanelVisible(false);
- mNotificationView.setVisibility(View.INVISIBLE);
- mNotificationView.setClipBounds(null);
- mNotificationViewController.onVisibilityChanged(false);
- // let the status bar know that the panel is closed
- setPanelExpanded(false);
- } else {
- mNotificationViewController.onVisibilityChanged(true);
- // let the status bar know that the panel is open
- mNotificationView.setVisibleNotificationsAsSeen();
- setPanelExpanded(true);
- }
- }
- });
- mFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity));
- animator.start();
+ // No op.
}
@Override
@@ -867,25 +418,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
@Override
protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
- registerNavBarListeners();
- }
-
- private void registerNavBarListeners() {
- // In CarStatusBar, navigation bars are built by NavigationBar.java
- // Instead, we register necessary callbacks to the navigation bar controller.
- mCarNavigationBarController.registerTopBarTouchListener(
- mTopNavBarNotificationTouchListener);
-
- mCarNavigationBarController.registerBottomBarTouchListener(
- mNavBarNotificationTouchListener);
-
- mCarNavigationBarController.registerLeftBarTouchListener(
- mNavBarNotificationTouchListener);
-
- mCarNavigationBarController.registerRightBarTouchListener(
- mNavBarNotificationTouchListener);
-
- mCarNavigationBarController.registerNotificationController(() -> togglePanel());
+ // No op.
}
@Override
@@ -966,245 +499,16 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
@Override
public void onDensityOrFontScaleChanged() {
super.onDensityOrFontScaleChanged();
- registerNavBarListeners();
// Need to update the background on density changed in case the change was due to night
// mode.
mNotificationPanelBackground = getDefaultWallpaper();
mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
}
- @Override
- public void onLocaleListChanged() {
- connectNotificationsUI();
- registerNavBarListeners();
- }
-
/**
* Returns the {@link Drawable} that represents the wallpaper that the user has currently set.
*/
private Drawable getDefaultWallpaper() {
return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
}
-
- private void setNotificationViewClipBounds(int height) {
- if (height > mNotificationView.getHeight()) {
- height = mNotificationView.getHeight();
- }
- Rect clipBounds = new Rect();
- clipBounds.set(0, 0, mNotificationView.getWidth(), height);
- // Sets the clip region on the notification list view.
- mNotificationView.setClipBounds(clipBounds);
- if (mHandleBar != null) {
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) mHandleBar.getLayoutParams();
- mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin);
- }
- if (mNotificationView.getHeight() > 0) {
- Drawable background = mNotificationView.getBackground().mutate();
- background.setAlpha((int) (getBackgroundAlpha(height) * 255));
- mNotificationView.setBackground(background);
- }
- }
-
- /**
- * Calculates the alpha value for the background based on how much of the notification
- * shade is visible to the user. When the notification shade is completely open then
- * alpha value will be 1.
- */
- private float getBackgroundAlpha(int height) {
- return mInitialBackgroundAlpha +
- ((float) height / mNotificationView.getHeight() * mBackgroundAlphaDiff);
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- super.onConfigChanged(newConfig);
-
- int uiModeNightMask = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK);
-
- boolean dayNightModeChanged = uiModeNightMask == Configuration.UI_MODE_NIGHT_YES
- || uiModeNightMask == Configuration.UI_MODE_NIGHT_NO;
-
- if (dayNightModeChanged) {
- mNotificationView.setBackgroundColor(
- mContext.getColor(R.color.notification_shade_background_color));
- }
- }
-
- private void calculatePercentageFromBottom(float height) {
- if (mNotificationView.getHeight() > 0) {
- mPercentageFromBottom = (int) Math.abs(
- height / mNotificationView.getHeight() * 100);
- }
- }
-
- private static final int SWIPE_DOWN_MIN_DISTANCE = 25;
- private static final int SWIPE_MAX_OFF_PATH = 75;
- private static final int SWIPE_THRESHOLD_VELOCITY = 200;
-
- /**
- * Only responsible for open hooks. Since once the panel opens it covers all elements
- * there is no need to merge with close.
- */
- private abstract class OpenNotificationGestureListener extends
- GestureDetector.SimpleOnGestureListener {
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
-
- if (mNotificationView.getVisibility() == View.INVISIBLE) {
- // when the on-scroll is called for the first time to open.
- mNotificationList.scrollToPosition(0);
- }
- mNotificationShadeWindowController.setPanelVisible(true);
- mNotificationView.setVisibility(View.VISIBLE);
-
- // clips the view for the notification shade when the user scrolls to open.
- setNotificationViewClipBounds((int) event2.getRawY());
-
- // Initially the scroll starts with height being zero. This checks protects from divide
- // by zero error.
- calculatePercentageFromBottom(event2.getRawY());
-
- mIsTracking = true;
- return true;
- }
-
-
- @Override
- public boolean onFling(MotionEvent event1, MotionEvent event2,
- float velocityX, float velocityY) {
- if (velocityY > SWIPE_THRESHOLD_VELOCITY) {
- mOpeningVelocity = velocityY;
- openNotification();
- return true;
- }
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
-
- return false;
- }
-
- protected abstract void openNotification();
- }
-
- /**
- * To be installed on the open panel notification panel
- */
- private abstract class CloseNotificationGestureListener extends
- GestureDetector.SimpleOnGestureListener {
-
- @Override
- public boolean onSingleTapUp(MotionEvent motionEvent) {
- if (mPanelExpanded) {
- animateNotificationPanel(DEFAULT_FLING_VELOCITY, true);
- }
- return true;
- }
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
- // should not clip while scroll to the bottom of the list.
- if (!mNotificationListAtBottomAtTimeOfTouch) {
- return false;
- }
- float actualNotificationHeight =
- mNotificationView.getHeight() - (event1.getRawY() - event2.getRawY());
- if (actualNotificationHeight > mNotificationView.getHeight()) {
- actualNotificationHeight = mNotificationView.getHeight();
- }
- if (mNotificationView.getHeight() > 0) {
- mPercentageFromBottom = (int) Math.abs(
- actualNotificationHeight / mNotificationView.getHeight() * 100);
- boolean isUp = distanceY > 0;
-
- // This check is to figure out if onScroll was called while swiping the card at
- // bottom of the list. At that time we should not allow notification shade to
- // close. We are also checking for the upwards swipe gesture here because it is
- // possible if a user is closing the notification shade and while swiping starts
- // to open again but does not fling. At that time we should allow the
- // notification shade to close fully or else it would stuck in between.
- if (Math.abs(mNotificationView.getHeight() - actualNotificationHeight)
- > SWIPE_DOWN_MIN_DISTANCE && isUp) {
- setNotificationViewClipBounds((int) actualNotificationHeight);
- mIsTracking = true;
- } else if (!isUp) {
- setNotificationViewClipBounds((int) actualNotificationHeight);
- }
- }
- // if we return true the the items in RV won't be scrollable.
- return false;
- }
-
-
- @Override
- public boolean onFling(MotionEvent event1, MotionEvent event2,
- float velocityX, float velocityY) {
- // should not fling if the touch does not start when view is at the bottom of the list.
- if (!mNotificationListAtBottomAtTimeOfTouch) {
- return false;
- }
- if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
- || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
- // swipe was not vertical or was not fast enough
- return false;
- }
- boolean isUp = velocityY < 0;
- if (isUp) {
- close();
- return true;
- } else {
- // we should close the shade
- animateNotificationPanel(velocityY, false);
- }
- return false;
- }
-
- protected abstract void close();
- }
-
- /**
- * To be installed on the nav bars.
- */
- private abstract class NavBarCloseNotificationGestureListener extends
- CloseNotificationGestureListener {
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- mClosingVelocity = DEFAULT_FLING_VELOCITY;
- if (mPanelExpanded) {
- close();
- }
- return super.onSingleTapUp(e);
- }
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
- calculatePercentageFromBottom(event2.getRawY());
- setNotificationViewClipBounds((int) event2.getRawY());
- return true;
- }
- }
-
- /**
- * To be installed on the handle bar.
- */
- private class HandleBarCloseNotificationGestureListener extends
- GestureDetector.SimpleOnGestureListener {
-
- @Override
- public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
- float distanceY) {
- calculatePercentageFromBottom(event2.getRawY());
- // To prevent the jump in the clip bounds while closing the notification shade using
- // the handle bar we should calculate the height using the diff of event1 and event2.
- // This will help the notification shade to clip smoothly as the event2 value changes
- // as event1 value will be fixed.
- int clipHeight =
- mNotificationView.getHeight() - (int) (event1.getRawY() - event2.getRawY());
- setNotificationViewClipBounds(clipHeight);
- return true;
- }
- }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 160268b8f461..1baa1f6891ee 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -23,9 +23,6 @@ import android.os.Handler;
import android.os.PowerManager;
import android.util.DisplayMetrics;
-import com.android.car.notification.CarNotificationListener;
-import com.android.car.notification.CarUxRestrictionManagerWrapper;
-import com.android.car.notification.NotificationDataManager;
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
@@ -34,7 +31,6 @@ import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.car.CarDeviceProvisionedController;
-import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.keyguard.DismissCallbackRegistry;
@@ -50,7 +46,6 @@ import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -205,13 +200,7 @@ public class CarStatusBarModule {
KeyguardIndicationController keyguardIndicationController,
DismissCallbackRegistry dismissCallbackRegistry,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
- CarServiceProvider carServiceProvider,
- Lazy<PowerManagerHelper> powerManagerHelperLazy,
- CarNavigationBarController carNavigationBarController,
- FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
- NotificationDataManager notificationDataManager,
- CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
- CarNotificationListener carNotificationListener) {
+ CarNavigationBarController carNavigationBarController) {
return new CarStatusBar(
context,
notificationsController,
@@ -289,12 +278,6 @@ public class CarStatusBarModule {
keyguardIndicationController,
dismissCallbackRegistry,
statusBarTouchableRegionManager,
- carServiceProvider,
- powerManagerHelperLazy,
- carNavigationBarController,
- flingAnimationUtilsBuilder,
- notificationDataManager,
- carUxRestrictionManagerWrapper,
- carNotificationListener);
+ carNavigationBarController);
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
index 71847bbac9fd..615a7bae2930 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/PowerManagerHelper.java
@@ -42,7 +42,7 @@ public class PowerManagerHelper {
private final CarServiceProvider.CarServiceOnConnectedListener mCarServiceLifecycleListener;
@Inject
- PowerManagerHelper(CarServiceProvider carServiceProvider) {
+ public PowerManagerHelper(CarServiceProvider carServiceProvider) {
mCarServiceProvider = carServiceProvider;
mCarServiceLifecycleListener = car -> {
Log.d(TAG, "Car Service connected");
@@ -58,14 +58,14 @@ public class PowerManagerHelper {
/**
* Sets a {@link CarPowerStateListener}. Should be set before {@link #connectToCarService()}.
*/
- void setCarPowerStateListener(@NonNull CarPowerStateListener listener) {
+ public void setCarPowerStateListener(@NonNull CarPowerStateListener listener) {
mCarPowerStateListener = listener;
}
/**
* Connect to Car service.
*/
- void connectToCarService() {
+ public void connectToCarService() {
mCarServiceProvider.addListener(mCarServiceLifecycleListener);
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewController.java
index 6e0db4f317ba..15ef0be38d4d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewController.java
@@ -120,4 +120,9 @@ public class OverlayViewController {
protected final View getLayout() {
return mLayout;
}
+
+ /** Returns the {@link OverlayViewGlobalStateController}. */
+ protected final OverlayViewGlobalStateController getOverlayViewGlobalStateController() {
+ return mOverlayViewGlobalStateController;
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java
index 2d4a9e6331d2..402d742cb949 100644
--- a/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayViewGlobalStateController.java
@@ -71,12 +71,10 @@ public class OverlayViewGlobalStateController {
public void showView(OverlayViewController viewController, Runnable show) {
if (mShownSet.isEmpty()) {
mCarNavigationBarController.hideBars();
- mSystemUIOverlayWindowController.setWindowExpanded(true);
+ setWindowVisible(true);
}
- if (!viewController.isInflated()) {
- viewController.inflate(mSystemUIOverlayWindowController.getBaseLayout());
- }
+ inflateView(viewController);
show.run();
mShownSet.add(viewController.getClass().getName());
@@ -104,9 +102,36 @@ public class OverlayViewGlobalStateController {
if (mShownSet.isEmpty()) {
mCarNavigationBarController.showBars();
- mSystemUIOverlayWindowController.setWindowExpanded(false);
+ setWindowVisible(false);
}
Log.d(TAG, "Content hidden: " + viewController.getClass().getName());
}
+
+ /** Sets the window visibility state. */
+ public void setWindowVisible(boolean expanded) {
+ mSystemUIOverlayWindowController.setWindowVisible(expanded);
+ }
+
+ /** Returns {@code true} is the window is visible. */
+ public boolean isWindowVisible() {
+ return mSystemUIOverlayWindowController.isWindowVisible();
+ }
+
+ /** Sets the focusable flag of the sysui overlawy window. */
+ public void setWindowFocusable(boolean focusable) {
+ mSystemUIOverlayWindowController.setWindowFocusable(focusable);
+ }
+
+ /** Returns {@code true} if the window is focusable. */
+ public boolean isWindowFocusable() {
+ return mSystemUIOverlayWindowController.isWindowFocusable();
+ }
+
+ /** Inflates the view controlled by the given view controller. */
+ public void inflateView(OverlayViewController viewController) {
+ if (!viewController.isInflated()) {
+ viewController.inflate(mSystemUIOverlayWindowController.getBaseLayout());
+ }
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayWindowModule.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayWindowModule.java
index b0e308966477..dd29f8dac387 100644
--- a/packages/CarSystemUI/src/com/android/systemui/window/OverlayWindowModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayWindowModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.window;
+import com.android.systemui.car.notification.NotificationPanelViewMediator;
import com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator;
import dagger.Binds;
@@ -28,10 +29,17 @@ import dagger.multibindings.IntoMap;
*/
@Module
public abstract class OverlayWindowModule {
- /** Inject into FullscreenUserSwitcherViewsMediator. */
+ /** Injects FullscreenUserSwitcherViewsMediator. */
@Binds
@IntoMap
@ClassKey(FullscreenUserSwitcherViewMediator.class)
public abstract OverlayViewMediator bindFullscreenUserSwitcherViewsMediator(
FullscreenUserSwitcherViewMediator overlayViewsMediator);
+
+ /** Injects NotificationPanelViewMediator. */
+ @Binds
+ @IntoMap
+ @ClassKey(NotificationPanelViewMediator.class)
+ public abstract OverlayViewMediator bindNotificationPanelViewMediator(
+ NotificationPanelViewMediator notificationPanelViewMediator);
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java
index 9c456eecc1ac..0dbe1a3ea1dd 100644
--- a/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/window/SystemUIOverlayWindowController.java
@@ -19,17 +19,15 @@ package com.android.systemui.window;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.PixelFormat;
-import android.graphics.Point;
import android.os.Binder;
import android.view.Gravity;
import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.policy.ConfigurationController;
import javax.inject.Inject;
@@ -45,36 +43,24 @@ public class SystemUIOverlayWindowController implements
ConfigurationController.ConfigurationListener {
private final Context mContext;
- private final Resources mResources;
private final WindowManager mWindowManager;
- private final int mStatusBarHeight;
- private final int mNavBarHeight;
- private final int mDisplayHeight;
private ViewGroup mBaseLayout;
private WindowManager.LayoutParams mLp;
private WindowManager.LayoutParams mLpChanged;
private boolean mIsAttached = false;
+ private boolean mVisible = false;
+ private boolean mFocusable = false;
@Inject
public SystemUIOverlayWindowController(
Context context,
- @Main Resources resources,
WindowManager windowManager,
ConfigurationController configurationController
) {
mContext = context;
- mResources = resources;
mWindowManager = windowManager;
- Point display = new Point();
- mWindowManager.getDefaultDisplay().getSize(display);
- mDisplayHeight = display.y;
-
- mStatusBarHeight = mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.navigation_bar_height);
-
mLpChanged = new WindowManager.LayoutParams();
mBaseLayout = (ViewGroup) LayoutInflater.from(context)
.inflate(R.layout.sysui_overlay_window, /* root= */ null, false);
@@ -103,13 +89,13 @@ public class SystemUIOverlayWindowController implements
// hardware-accelerated.
mLp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
- mStatusBarHeight,
+ ViewGroup.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
+ | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
@@ -121,26 +107,38 @@ public class SystemUIOverlayWindowController implements
mWindowManager.addView(mBaseLayout, mLp);
mLpChanged.copyFrom(mLp);
+ setWindowVisible(false);
+ }
+
+ /** Sets the window to the visible state. */
+ public void setWindowVisible(boolean visible) {
+ mVisible = visible;
+ if (visible) {
+ mBaseLayout.setVisibility(View.VISIBLE);
+ } else {
+ mBaseLayout.setVisibility(View.INVISIBLE);
+ }
+ updateWindow();
}
- /** Sets the window to the expanded state. */
- public void setWindowExpanded(boolean expanded) {
- if (expanded) {
- // TODO: Update this so that the windowing type gets the full height of the display
- // when we use MATCH_PARENT.
- mLpChanged.height = mDisplayHeight + mNavBarHeight;
- mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ /** Sets the window to be focusable. */
+ public void setWindowFocusable(boolean focusable) {
+ mFocusable = focusable;
+ if (focusable) {
+ mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
} else {
- mLpChanged.height = mStatusBarHeight;
- // TODO: Allow touches to go through to the status bar to handle notification panel.
- mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
}
updateWindow();
}
- /** Returns {@code true} if the window is expanded */
- public boolean isWindowExpanded() {
- return mLp.height != mStatusBarHeight;
+ /** Returns {@code true} if the window is visible */
+ public boolean isWindowVisible() {
+ return mVisible;
+ }
+
+ public boolean isWindowFocusable() {
+ return mFocusable;
}
private void updateWindow() {
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewGlobalStateControllerTest.java
index 03354d1aa9d7..a96b90636891 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewGlobalStateControllerTest.java
@@ -97,7 +97,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
public void showView_nothingAlreadyShown_windowIsExpanded() {
mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
- verify(mSystemUIOverlayWindowController).setWindowExpanded(true);
+ verify(mSystemUIOverlayWindowController).setWindowVisible(true);
}
@Test
@@ -115,7 +115,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.showView(mOverlayViewController, mRunnable);
- verify(mSystemUIOverlayWindowController, never()).setWindowExpanded(true);
+ verify(mSystemUIOverlayWindowController, never()).setWindowVisible(true);
}
@Test
@@ -223,7 +223,7 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
- verify(mSystemUIOverlayWindowController, never()).setWindowExpanded(false);
+ verify(mSystemUIOverlayWindowController, never()).setWindowVisible(false);
}
@Test
@@ -245,6 +245,24 @@ public class OverlayViewGlobalStateControllerTest extends SysuiTestCase {
mOverlayViewGlobalStateController.hideView(mOverlayViewController, mRunnable);
- verify(mSystemUIOverlayWindowController).setWindowExpanded(false);
+ verify(mSystemUIOverlayWindowController).setWindowVisible(false);
+ }
+
+ @Test
+ public void inflateView_notInflated_inflates() {
+ when(mOverlayViewController.isInflated()).thenReturn(false);
+
+ mOverlayViewGlobalStateController.inflateView(mOverlayViewController);
+
+ verify(mOverlayViewController).inflate(mBaseLayout);
+ }
+
+ @Test
+ public void inflateView_alreadyInflated_doesNotInflate() {
+ when(mOverlayViewController.isInflated()).thenReturn(true);
+
+ mOverlayViewGlobalStateController.inflateView(mOverlayViewController);
+
+ verify(mOverlayViewController, never()).inflate(mBaseLayout);
}
}