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