1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.statusbar.phone; 18 19 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; 20 21 import android.animation.Animator; 22 import android.animation.AnimatorListenerAdapter; 23 import android.annotation.Nullable; 24 import android.view.View; 25 import android.view.WindowInsetsController.Appearance; 26 import android.view.WindowInsetsController.Behavior; 27 import android.view.WindowManager; 28 import android.view.animation.AccelerateInterpolator; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.internal.statusbar.NotificationVisibility; 32 import com.android.internal.view.AppearanceRegion; 33 import com.android.systemui.dagger.SysUISingleton; 34 import com.android.systemui.statusbar.CommandQueue; 35 import com.android.systemui.statusbar.notification.NotificationEntryListener; 36 import com.android.systemui.statusbar.notification.NotificationEntryManager; 37 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 38 39 import javax.inject.Inject; 40 41 /** 42 * Apps can request a low profile mode {@link View.SYSTEM_UI_FLAG_LOW_PROFILE} 43 * where status bar and navigation icons dim. In this mode, a notification dot appears 44 * where the notification icons would appear if they would be shown outside of this mode. 45 * 46 * This controller shows and hides the notification dot in the status bar to indicate 47 * whether there are notifications when the device is in {@link View.SYSTEM_UI_FLAG_LOW_PROFILE}. 48 */ 49 @SysUISingleton 50 public class LightsOutNotifController { 51 private final CommandQueue mCommandQueue; 52 private final NotificationEntryManager mEntryManager; 53 private final WindowManager mWindowManager; 54 55 /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */ 56 @VisibleForTesting @Appearance int mAppearance; 57 58 private int mDisplayId; 59 private View mLightsOutNotifView; 60 61 @Inject LightsOutNotifController(WindowManager windowManager, NotificationEntryManager entryManager, CommandQueue commandQueue)62 LightsOutNotifController(WindowManager windowManager, 63 NotificationEntryManager entryManager, 64 CommandQueue commandQueue) { 65 mWindowManager = windowManager; 66 mEntryManager = entryManager; 67 mCommandQueue = commandQueue; 68 } 69 70 /** 71 * Sets the notification dot view after it is created in the StatusBar. 72 * This is the view this controller will show and hide depending on whether: 73 * 1. there are active notifications 74 * 2. an app has requested {@link View.SYSTEM_UI_FLAG_LOW_PROFILE} 75 */ setLightsOutNotifView(View lightsOutNotifView)76 void setLightsOutNotifView(View lightsOutNotifView) { 77 destroy(); 78 mLightsOutNotifView = lightsOutNotifView; 79 80 if (mLightsOutNotifView != null) { 81 mLightsOutNotifView.setVisibility(View.GONE); 82 mLightsOutNotifView.setAlpha(0f); 83 init(); 84 } 85 } 86 destroy()87 private void destroy() { 88 mEntryManager.removeNotificationEntryListener(mEntryListener); 89 mCommandQueue.removeCallback(mCallback); 90 } 91 init()92 private void init() { 93 mDisplayId = mWindowManager.getDefaultDisplay().getDisplayId(); 94 mEntryManager.addNotificationEntryListener(mEntryListener); 95 mCommandQueue.addCallback(mCallback); 96 97 updateLightsOutView(); 98 } 99 hasActiveNotifications()100 private boolean hasActiveNotifications() { 101 return mEntryManager.hasActiveNotifications(); 102 } 103 104 @VisibleForTesting updateLightsOutView()105 void updateLightsOutView() { 106 if (mLightsOutNotifView == null) { 107 return; 108 } 109 110 final boolean showDot = shouldShowDot(); 111 if (showDot != isShowingDot()) { 112 if (showDot) { 113 mLightsOutNotifView.setAlpha(0f); 114 mLightsOutNotifView.setVisibility(View.VISIBLE); 115 } 116 117 mLightsOutNotifView.animate() 118 .alpha(showDot ? 1 : 0) 119 .setDuration(showDot ? 750 : 250) 120 .setInterpolator(new AccelerateInterpolator(2.0f)) 121 .setListener(new AnimatorListenerAdapter() { 122 @Override 123 public void onAnimationEnd(Animator a) { 124 mLightsOutNotifView.setAlpha(showDot ? 1 : 0); 125 mLightsOutNotifView.setVisibility(showDot ? View.VISIBLE : View.GONE); 126 } 127 }) 128 .start(); 129 } 130 } 131 132 @VisibleForTesting isShowingDot()133 boolean isShowingDot() { 134 return mLightsOutNotifView.getVisibility() == View.VISIBLE 135 && mLightsOutNotifView.getAlpha() == 1.0f; 136 } 137 138 @VisibleForTesting shouldShowDot()139 boolean shouldShowDot() { 140 return hasActiveNotifications() && areLightsOut(); 141 } 142 143 @VisibleForTesting areLightsOut()144 boolean areLightsOut() { 145 return 0 != (mAppearance & APPEARANCE_LOW_PROFILE_BARS); 146 } 147 148 private final CommandQueue.Callbacks mCallback = new CommandQueue.Callbacks() { 149 @Override 150 public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, 151 AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, 152 @Behavior int behavior, boolean isFullscreen) { 153 if (displayId != mDisplayId) { 154 return; 155 } 156 mAppearance = appearance; 157 updateLightsOutView(); 158 } 159 }; 160 161 private final NotificationEntryListener mEntryListener = new NotificationEntryListener() { 162 // Cares about notifications post-filtering 163 @Override 164 public void onNotificationAdded(NotificationEntry entry) { 165 updateLightsOutView(); 166 } 167 168 @Override 169 public void onPostEntryUpdated(NotificationEntry entry) { 170 updateLightsOutView(); 171 } 172 173 @Override 174 public void onEntryRemoved(@Nullable NotificationEntry entry, 175 NotificationVisibility visibility, boolean removedByUser, int reason) { 176 updateLightsOutView(); 177 } 178 }; 179 } 180