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