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