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.InsetsVisibilities; 25 import android.view.View; 26 import android.view.WindowInsetsController.Appearance; 27 import android.view.WindowInsetsController.Behavior; 28 import android.view.WindowManager; 29 import android.view.animation.AccelerateInterpolator; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.statusbar.NotificationVisibility; 33 import com.android.internal.view.AppearanceRegion; 34 import com.android.systemui.dagger.SysUISingleton; 35 import com.android.systemui.statusbar.CommandQueue; 36 import com.android.systemui.statusbar.notification.NotificationEntryListener; 37 import com.android.systemui.statusbar.notification.NotificationEntryManager; 38 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 39 40 import javax.inject.Inject; 41 42 /** 43 * Apps can request a low profile mode {@link View.SYSTEM_UI_FLAG_LOW_PROFILE} 44 * where status bar and navigation icons dim. In this mode, a notification dot appears 45 * where the notification icons would appear if they would be shown outside of this mode. 46 * 47 * This controller shows and hides the notification dot in the status bar to indicate 48 * whether there are notifications when the device is in {@link View.SYSTEM_UI_FLAG_LOW_PROFILE}. 49 */ 50 @SysUISingleton 51 public class LightsOutNotifController { 52 private final CommandQueue mCommandQueue; 53 private final NotificationEntryManager mEntryManager; 54 private final WindowManager mWindowManager; 55 56 /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int) */ 57 @VisibleForTesting @Appearance int mAppearance; 58 59 private int mDisplayId; 60 private View mLightsOutNotifView; 61 62 @Inject LightsOutNotifController(WindowManager windowManager, NotificationEntryManager entryManager, CommandQueue commandQueue)63 LightsOutNotifController(WindowManager windowManager, 64 NotificationEntryManager entryManager, 65 CommandQueue commandQueue) { 66 mWindowManager = windowManager; 67 mEntryManager = entryManager; 68 mCommandQueue = commandQueue; 69 } 70 71 /** 72 * Sets the notification dot view after it is created in the StatusBar. 73 * This is the view this controller will show and hide depending on whether: 74 * 1. there are active notifications 75 * 2. an app has requested {@link View.SYSTEM_UI_FLAG_LOW_PROFILE} 76 */ setLightsOutNotifView(View lightsOutNotifView)77 void setLightsOutNotifView(View lightsOutNotifView) { 78 destroy(); 79 mLightsOutNotifView = lightsOutNotifView; 80 81 if (mLightsOutNotifView != null) { 82 mLightsOutNotifView.setVisibility(View.GONE); 83 mLightsOutNotifView.setAlpha(0f); 84 init(); 85 } 86 } 87 destroy()88 private void destroy() { 89 mEntryManager.removeNotificationEntryListener(mEntryListener); 90 mCommandQueue.removeCallback(mCallback); 91 } 92 init()93 private void init() { 94 mDisplayId = mWindowManager.getDefaultDisplay().getDisplayId(); 95 mEntryManager.addNotificationEntryListener(mEntryListener); 96 mCommandQueue.addCallback(mCallback); 97 98 updateLightsOutView(); 99 } 100 hasActiveNotifications()101 private boolean hasActiveNotifications() { 102 return mEntryManager.hasActiveNotifications(); 103 } 104 105 @VisibleForTesting updateLightsOutView()106 void updateLightsOutView() { 107 if (mLightsOutNotifView == null) { 108 return; 109 } 110 111 final boolean showDot = shouldShowDot(); 112 if (showDot != isShowingDot()) { 113 if (showDot) { 114 mLightsOutNotifView.setAlpha(0f); 115 mLightsOutNotifView.setVisibility(View.VISIBLE); 116 } 117 118 mLightsOutNotifView.animate() 119 .alpha(showDot ? 1 : 0) 120 .setDuration(showDot ? 750 : 250) 121 .setInterpolator(new AccelerateInterpolator(2.0f)) 122 .setListener(new AnimatorListenerAdapter() { 123 @Override 124 public void onAnimationEnd(Animator a) { 125 mLightsOutNotifView.setAlpha(showDot ? 1 : 0); 126 mLightsOutNotifView.setVisibility(showDot ? View.VISIBLE : View.GONE); 127 // Unset the listener, otherwise this may persist for 128 // another view property animation 129 mLightsOutNotifView.animate().setListener(null); 130 } 131 }) 132 .start(); 133 } 134 } 135 136 @VisibleForTesting isShowingDot()137 boolean isShowingDot() { 138 return mLightsOutNotifView.getVisibility() == View.VISIBLE 139 && mLightsOutNotifView.getAlpha() == 1.0f; 140 } 141 142 @VisibleForTesting shouldShowDot()143 boolean shouldShowDot() { 144 return hasActiveNotifications() && areLightsOut(); 145 } 146 147 @VisibleForTesting areLightsOut()148 boolean areLightsOut() { 149 return 0 != (mAppearance & APPEARANCE_LOW_PROFILE_BARS); 150 } 151 152 private final CommandQueue.Callbacks mCallback = new CommandQueue.Callbacks() { 153 @Override 154 public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance, 155 AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme, 156 @Behavior int behavior, InsetsVisibilities requestedVisibilities, 157 String packageName) { 158 if (displayId != mDisplayId) { 159 return; 160 } 161 mAppearance = appearance; 162 updateLightsOutView(); 163 } 164 }; 165 166 private final NotificationEntryListener mEntryListener = new NotificationEntryListener() { 167 // Cares about notifications post-filtering 168 @Override 169 public void onNotificationAdded(NotificationEntry entry) { 170 updateLightsOutView(); 171 } 172 173 @Override 174 public void onPostEntryUpdated(NotificationEntry entry) { 175 updateLightsOutView(); 176 } 177 178 @Override 179 public void onEntryRemoved(@Nullable NotificationEntry entry, 180 NotificationVisibility visibility, boolean removedByUser, int reason) { 181 updateLightsOutView(); 182 } 183 }; 184 } 185