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