1 package com.android.car.notification; 2 3 import android.car.drivingstate.CarUxRestrictions; 4 import android.os.Build; 5 import android.os.Handler; 6 import android.os.Message; 7 import android.util.Log; 8 import android.view.View; 9 import android.widget.Toast; 10 11 import java.util.List; 12 import java.util.stream.Collectors; 13 14 /** 15 * This class is a bridge to collect signals from the notification and ux restriction services and 16 * trigger the correct UI updates. 17 */ 18 public class NotificationViewController { 19 20 private static final String TAG = "NotificationViewControl"; 21 private final CarNotificationView mCarNotificationView; 22 private final PreprocessingManager mPreprocessingManager; 23 private final CarNotificationListener mCarNotificationListener; 24 private CarUxRestrictionManagerWrapper mUxResitrictionListener; 25 private NotificationDataManager mNotificationDataManager; 26 private NotificationUpdateHandler mNotificationUpdateHandler = new NotificationUpdateHandler(); 27 private boolean mShowLessImportantNotifications; 28 private boolean mIsVisible; 29 NotificationViewController(CarNotificationView carNotificationView, PreprocessingManager preprocessingManager, CarNotificationListener carNotificationListener, CarUxRestrictionManagerWrapper uxResitrictionListener, NotificationDataManager notificationDataManager)30 public NotificationViewController(CarNotificationView carNotificationView, 31 PreprocessingManager preprocessingManager, 32 CarNotificationListener carNotificationListener, 33 CarUxRestrictionManagerWrapper uxResitrictionListener, 34 NotificationDataManager notificationDataManager) { 35 mCarNotificationView = carNotificationView; 36 mPreprocessingManager = preprocessingManager; 37 mCarNotificationListener = carNotificationListener; 38 mUxResitrictionListener = uxResitrictionListener; 39 mNotificationDataManager = notificationDataManager; 40 41 // Long clicking on the notification center title toggles hiding media, navigation, and 42 // less important (< IMPORTANCE_DEFAULT) ongoing foreground service notifications. 43 // This is only available for ENG and USERDEBUG builds. 44 View view = mCarNotificationView.findViewById(R.id.notification_center_title); 45 if (view != null && (Build.IS_ENG || Build.IS_USERDEBUG)) { 46 view.setOnLongClickListener(v -> { 47 mShowLessImportantNotifications = !mShowLessImportantNotifications; 48 Toast.makeText( 49 carNotificationView.getContext(), 50 "Foreground, navigation and media notifications " + ( 51 mShowLessImportantNotifications ? "ENABLED" : "DISABLED"), 52 Toast.LENGTH_SHORT).show(); 53 resetNotifications(mShowLessImportantNotifications); 54 return true; 55 }); 56 } 57 58 resetNotifications(mShowLessImportantNotifications); 59 } 60 61 /** 62 * Updates UI and registers required listeners 63 */ enable()64 public void enable() { 65 mCarNotificationListener.setHandler(mNotificationUpdateHandler); 66 mUxResitrictionListener.setCarNotificationView(mCarNotificationView); 67 try { 68 CarUxRestrictions currentRestrictions = 69 mUxResitrictionListener.getCurrentCarUxRestrictions(); 70 mCarNotificationView.onUxRestrictionsChanged(currentRestrictions); 71 } catch (RuntimeException e) { 72 Log.e(TAG, "Car not connected", e); 73 } 74 } 75 76 /** 77 * Remove listeners. 78 */ disable()79 public void disable() { 80 mCarNotificationListener.setHandler(null); 81 mUxResitrictionListener.setCarNotificationView(null); 82 } 83 84 /** 85 * Called when the notification view's visibility is changed. 86 */ onVisibilityChanged(boolean isVisible)87 public void onVisibilityChanged(boolean isVisible) { 88 mIsVisible = isVisible; 89 // Reset and collapse all groups when notification view disappears. 90 if (!mIsVisible) { 91 resetNotifications(mShowLessImportantNotifications); 92 mCarNotificationView.resetState(); 93 } 94 } 95 96 /** 97 * Reset notifications to the latest state. 98 */ resetNotifications(boolean showLessImportantNotifications)99 private void resetNotifications(boolean showLessImportantNotifications) { 100 mPreprocessingManager.init( 101 mCarNotificationListener.getNotifications(), 102 mCarNotificationListener.getCurrentRanking()); 103 104 List<NotificationGroup> notificationGroups = mPreprocessingManager.process( 105 showLessImportantNotifications, 106 mCarNotificationListener.getNotifications(), 107 mCarNotificationListener.getCurrentRanking()); 108 109 List<NotificationGroup> unseenNotifications = notificationGroups.stream() 110 .filter(g -> g.getChildNotifications().stream() 111 .anyMatch(mCarNotificationListener::shouldTrackUnseen)) 112 .collect(Collectors.toList()); 113 114 mNotificationDataManager.updateUnseenNotification(unseenNotifications); 115 mCarNotificationView.setNotifications(notificationGroups); 116 } 117 118 /** 119 * Update notifications: no grouping/ranking updates will go through. 120 * Insertion, deletion and content update will apply immediately. 121 */ updateNotifications( boolean showLessImportantNotifications, int what, AlertEntry alertEntry)122 private void updateNotifications( 123 boolean showLessImportantNotifications, int what, AlertEntry alertEntry) { 124 125 if (mPreprocessingManager.shouldFilter(alertEntry, 126 mCarNotificationListener.getCurrentRanking())) { 127 // if the new notification should be filtered out, return early 128 return; 129 } 130 131 mCarNotificationView.setNotifications( 132 mPreprocessingManager.updateNotifications( 133 showLessImportantNotifications, 134 alertEntry, 135 what, 136 mCarNotificationListener.getCurrentRanking())); 137 } 138 139 private class NotificationUpdateHandler extends Handler { 140 @Override handleMessage(Message message)141 public void handleMessage(Message message) { 142 if (mIsVisible) { 143 updateNotifications( 144 mShowLessImportantNotifications, 145 message.what, 146 (AlertEntry) message.obj); 147 } else { 148 resetNotifications(mShowLessImportantNotifications); 149 } 150 } 151 } 152 } 153