1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.statusbar.phone; 16 17 import static android.app.StatusBarManager.DISABLE_CLOCK; 18 import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS; 19 import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO; 20 21 import android.annotation.Nullable; 22 import android.app.Fragment; 23 import android.os.Bundle; 24 import android.os.Parcelable; 25 import android.util.SparseArray; 26 import android.view.LayoutInflater; 27 import android.view.View; 28 import android.view.ViewGroup; 29 import android.view.ViewStub; 30 import android.widget.LinearLayout; 31 32 import com.android.systemui.Dependency; 33 import com.android.systemui.Interpolators; 34 import com.android.systemui.R; 35 import com.android.systemui.SysUiServiceProvider; 36 import com.android.systemui.plugins.statusbar.StatusBarStateController; 37 import com.android.systemui.statusbar.CommandQueue; 38 import com.android.systemui.statusbar.StatusBarState; 39 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; 40 import com.android.systemui.statusbar.policy.EncryptionHelper; 41 import com.android.systemui.statusbar.policy.KeyguardMonitor; 42 import com.android.systemui.statusbar.policy.NetworkController; 43 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; 44 45 /** 46 * Contains the collapsed status bar and handles hiding/showing based on disable flags 47 * and keyguard state. Also manages lifecycle to make sure the views it contains are being 48 * updated by the StatusBarIconController and DarkIconManager while it is attached. 49 */ 50 public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks, 51 StatusBarStateController.StateListener { 52 53 public static final String TAG = "CollapsedStatusBarFragment"; 54 private static final String EXTRA_PANEL_STATE = "panel_state"; 55 public static final String STATUS_BAR_ICON_MANAGER_TAG = "status_bar_icon_manager"; 56 public static final int FADE_IN_DURATION = 320; 57 public static final int FADE_IN_DELAY = 50; 58 private PhoneStatusBarView mStatusBar; 59 private StatusBarStateController mStatusBarStateController; 60 private KeyguardMonitor mKeyguardMonitor; 61 private NetworkController mNetworkController; 62 private LinearLayout mSystemIconArea; 63 private View mClockView; 64 private View mNotificationIconAreaInner; 65 private View mCenteredIconArea; 66 private int mDisabled1; 67 private StatusBar mStatusBarComponent; 68 private DarkIconManager mDarkIconManager; 69 private View mOperatorNameFrame; 70 private CommandQueue mCommandQueue; 71 72 private SignalCallback mSignalCallback = new SignalCallback() { 73 @Override 74 public void setIsAirplaneMode(NetworkController.IconState icon) { 75 mCommandQueue.recomputeDisableFlags(getContext().getDisplayId(), true /* animate */); 76 } 77 }; 78 79 @Override onCreate(@ullable Bundle savedInstanceState)80 public void onCreate(@Nullable Bundle savedInstanceState) { 81 super.onCreate(savedInstanceState); 82 mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); 83 mNetworkController = Dependency.get(NetworkController.class); 84 mStatusBarStateController = Dependency.get(StatusBarStateController.class); 85 mStatusBarComponent = SysUiServiceProvider.getComponent(getContext(), StatusBar.class); 86 mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class); 87 } 88 89 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState)90 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 91 Bundle savedInstanceState) { 92 return inflater.inflate(R.layout.status_bar, container, false); 93 } 94 95 @Override onViewCreated(View view, @Nullable Bundle savedInstanceState)96 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 97 super.onViewCreated(view, savedInstanceState); 98 mStatusBar = (PhoneStatusBarView) view; 99 if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) { 100 mStatusBar.restoreHierarchyState( 101 savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE)); 102 } 103 mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons)); 104 mDarkIconManager.setShouldLog(true); 105 Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager); 106 mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area); 107 mClockView = mStatusBar.findViewById(R.id.clock); 108 showSystemIconArea(false); 109 showClock(false); 110 initEmergencyCryptkeeperText(); 111 initOperatorName(); 112 } 113 114 @Override onSaveInstanceState(Bundle outState)115 public void onSaveInstanceState(Bundle outState) { 116 super.onSaveInstanceState(outState); 117 SparseArray<Parcelable> states = new SparseArray<>(); 118 mStatusBar.saveHierarchyState(states); 119 outState.putSparseParcelableArray(EXTRA_PANEL_STATE, states); 120 } 121 122 @Override onResume()123 public void onResume() { 124 super.onResume(); 125 mCommandQueue.addCallback(this); 126 mStatusBarStateController.addCallback(this); 127 } 128 129 @Override onPause()130 public void onPause() { 131 super.onPause(); 132 mCommandQueue.removeCallback(this); 133 mStatusBarStateController.removeCallback(this); 134 } 135 136 @Override onDestroyView()137 public void onDestroyView() { 138 super.onDestroyView(); 139 Dependency.get(StatusBarIconController.class).removeIconGroup(mDarkIconManager); 140 if (mNetworkController.hasEmergencyCryptKeeperText()) { 141 mNetworkController.removeCallback(mSignalCallback); 142 } 143 } 144 initNotificationIconArea(NotificationIconAreaController notificationIconAreaController)145 public void initNotificationIconArea(NotificationIconAreaController 146 notificationIconAreaController) { 147 ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area); 148 mNotificationIconAreaInner = 149 notificationIconAreaController.getNotificationInnerAreaView(); 150 if (mNotificationIconAreaInner.getParent() != null) { 151 ((ViewGroup) mNotificationIconAreaInner.getParent()) 152 .removeView(mNotificationIconAreaInner); 153 } 154 notificationIconArea.addView(mNotificationIconAreaInner); 155 156 ViewGroup statusBarCenteredIconArea = mStatusBar.findViewById(R.id.centered_icon_area); 157 mCenteredIconArea = notificationIconAreaController.getCenteredNotificationAreaView(); 158 if (mCenteredIconArea.getParent() != null) { 159 ((ViewGroup) mCenteredIconArea.getParent()) 160 .removeView(mCenteredIconArea); 161 } 162 statusBarCenteredIconArea.addView(mCenteredIconArea); 163 164 // Default to showing until we know otherwise. 165 showNotificationIconArea(false); 166 } 167 168 @Override disable(int displayId, int state1, int state2, boolean animate)169 public void disable(int displayId, int state1, int state2, boolean animate) { 170 if (displayId != getContext().getDisplayId()) { 171 return; 172 } 173 state1 = adjustDisableFlags(state1); 174 final int old1 = mDisabled1; 175 final int diff1 = state1 ^ old1; 176 mDisabled1 = state1; 177 if ((diff1 & DISABLE_SYSTEM_INFO) != 0) { 178 if ((state1 & DISABLE_SYSTEM_INFO) != 0) { 179 hideSystemIconArea(animate); 180 hideOperatorName(animate); 181 } else { 182 showSystemIconArea(animate); 183 showOperatorName(animate); 184 } 185 } 186 if ((diff1 & DISABLE_NOTIFICATION_ICONS) != 0) { 187 if ((state1 & DISABLE_NOTIFICATION_ICONS) != 0) { 188 hideNotificationIconArea(animate); 189 } else { 190 showNotificationIconArea(animate); 191 } 192 } 193 // The clock may have already been hidden, but we might want to shift its 194 // visibility to GONE from INVISIBLE or vice versa 195 if ((diff1 & DISABLE_CLOCK) != 0 || mClockView.getVisibility() != clockHiddenMode()) { 196 if ((state1 & DISABLE_CLOCK) != 0) { 197 hideClock(animate); 198 } else { 199 showClock(animate); 200 } 201 } 202 } 203 adjustDisableFlags(int state)204 protected int adjustDisableFlags(int state) { 205 boolean headsUpVisible = mStatusBarComponent.headsUpShouldBeVisible(); 206 if (headsUpVisible) { 207 state |= DISABLE_CLOCK; 208 } 209 210 if (!mKeyguardMonitor.isLaunchTransitionFadingAway() 211 && !mKeyguardMonitor.isKeyguardFadingAway() 212 && shouldHideNotificationIcons() 213 && !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD 214 && headsUpVisible)) { 215 state |= DISABLE_NOTIFICATION_ICONS; 216 state |= DISABLE_SYSTEM_INFO; 217 state |= DISABLE_CLOCK; 218 } 219 220 221 if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) { 222 if (mNetworkController.hasEmergencyCryptKeeperText()) { 223 state |= DISABLE_NOTIFICATION_ICONS; 224 } 225 if (!mNetworkController.isRadioOn()) { 226 state |= DISABLE_SYSTEM_INFO; 227 } 228 } 229 230 // The shelf will be hidden when dozing with a custom clock, we must show notification 231 // icons in this occasion. 232 if (mStatusBarStateController.isDozing() 233 && mStatusBarComponent.getPanel().hasCustomClock()) { 234 state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO; 235 } 236 237 return state; 238 } 239 shouldHideNotificationIcons()240 private boolean shouldHideNotificationIcons() { 241 if (!mStatusBar.isClosed() && mStatusBarComponent.hideStatusBarIconsWhenExpanded()) { 242 return true; 243 } 244 if (mStatusBarComponent.hideStatusBarIconsForBouncer()) { 245 return true; 246 } 247 return false; 248 } 249 hideSystemIconArea(boolean animate)250 public void hideSystemIconArea(boolean animate) { 251 animateHide(mSystemIconArea, animate); 252 } 253 showSystemIconArea(boolean animate)254 public void showSystemIconArea(boolean animate) { 255 animateShow(mSystemIconArea, animate); 256 } 257 hideClock(boolean animate)258 public void hideClock(boolean animate) { 259 animateHiddenState(mClockView, clockHiddenMode(), animate); 260 } 261 showClock(boolean animate)262 public void showClock(boolean animate) { 263 animateShow(mClockView, animate); 264 } 265 266 /** 267 * If panel is expanded/expanding it usually means QS shade is opening, so 268 * don't set the clock GONE otherwise it'll mess up the animation. 269 */ clockHiddenMode()270 private int clockHiddenMode() { 271 if (!mStatusBar.isClosed() && !mKeyguardMonitor.isShowing() 272 && !mStatusBarStateController.isDozing()) { 273 return View.INVISIBLE; 274 } 275 return View.GONE; 276 } 277 hideNotificationIconArea(boolean animate)278 public void hideNotificationIconArea(boolean animate) { 279 animateHide(mNotificationIconAreaInner, animate); 280 animateHide(mCenteredIconArea, animate); 281 } 282 showNotificationIconArea(boolean animate)283 public void showNotificationIconArea(boolean animate) { 284 animateShow(mNotificationIconAreaInner, animate); 285 animateShow(mCenteredIconArea, animate); 286 } 287 hideOperatorName(boolean animate)288 public void hideOperatorName(boolean animate) { 289 if (mOperatorNameFrame != null) { 290 animateHide(mOperatorNameFrame, animate); 291 } 292 } 293 showOperatorName(boolean animate)294 public void showOperatorName(boolean animate) { 295 if (mOperatorNameFrame != null) { 296 animateShow(mOperatorNameFrame, animate); 297 } 298 } 299 300 /** 301 * Animate a view to INVISIBLE or GONE 302 */ animateHiddenState(final View v, int state, boolean animate)303 private void animateHiddenState(final View v, int state, boolean animate) { 304 v.animate().cancel(); 305 if (!animate) { 306 v.setAlpha(0f); 307 v.setVisibility(state); 308 return; 309 } 310 311 v.animate() 312 .alpha(0f) 313 .setDuration(160) 314 .setStartDelay(0) 315 .setInterpolator(Interpolators.ALPHA_OUT) 316 .withEndAction(() -> v.setVisibility(state)); 317 } 318 319 /** 320 * Hides a view. 321 */ animateHide(final View v, boolean animate)322 private void animateHide(final View v, boolean animate) { 323 animateHiddenState(v, View.INVISIBLE, animate); 324 } 325 326 /** 327 * Shows a view, and synchronizes the animation with Keyguard exit animations, if applicable. 328 */ animateShow(View v, boolean animate)329 private void animateShow(View v, boolean animate) { 330 v.animate().cancel(); 331 v.setVisibility(View.VISIBLE); 332 if (!animate) { 333 v.setAlpha(1f); 334 return; 335 } 336 v.animate() 337 .alpha(1f) 338 .setDuration(FADE_IN_DURATION) 339 .setInterpolator(Interpolators.ALPHA_IN) 340 .setStartDelay(FADE_IN_DELAY) 341 342 // We need to clean up any pending end action from animateHide if we call 343 // both hide and show in the same frame before the animation actually gets started. 344 // cancel() doesn't really remove the end action. 345 .withEndAction(null); 346 347 // Synchronize the motion with the Keyguard fading if necessary. 348 if (mKeyguardMonitor.isKeyguardFadingAway()) { 349 v.animate() 350 .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration()) 351 .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN) 352 .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay()) 353 .start(); 354 } 355 } 356 initEmergencyCryptkeeperText()357 private void initEmergencyCryptkeeperText() { 358 View emergencyViewStub = mStatusBar.findViewById(R.id.emergency_cryptkeeper_text); 359 if (mNetworkController.hasEmergencyCryptKeeperText()) { 360 if (emergencyViewStub != null) { 361 ((ViewStub) emergencyViewStub).inflate(); 362 } 363 mNetworkController.addCallback(mSignalCallback); 364 } else if (emergencyViewStub != null) { 365 ViewGroup parent = (ViewGroup) emergencyViewStub.getParent(); 366 parent.removeView(emergencyViewStub); 367 } 368 } 369 initOperatorName()370 private void initOperatorName() { 371 if (getResources().getBoolean(R.bool.config_showOperatorNameInStatusBar)) { 372 ViewStub stub = mStatusBar.findViewById(R.id.operator_name); 373 mOperatorNameFrame = stub.inflate(); 374 } 375 } 376 377 @Override onStateChanged(int newState)378 public void onStateChanged(int newState) { 379 380 } 381 382 @Override onDozingChanged(boolean isDozing)383 public void onDozingChanged(boolean isDozing) { 384 disable(getContext().getDisplayId(), mDisabled1, mDisabled1, false /* animate */); 385 } 386 } 387