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 com.android.systemui.statusbar.phone.StatusBar.reinflateSignalCluster;
18
19import android.annotation.Nullable;
20import android.app.Fragment;
21import android.app.StatusBarManager;
22import android.os.Bundle;
23import android.view.LayoutInflater;
24import android.view.View;
25import android.view.ViewGroup;
26import android.view.ViewStub;
27import android.widget.LinearLayout;
28
29import com.android.systemui.Dependency;
30import com.android.systemui.Interpolators;
31import com.android.systemui.R;
32import com.android.systemui.SysUiServiceProvider;
33import com.android.systemui.statusbar.CommandQueue;
34import com.android.systemui.statusbar.SignalClusterView;
35import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
36import com.android.systemui.statusbar.policy.DarkIconDispatcher;
37import com.android.systemui.statusbar.policy.EncryptionHelper;
38import com.android.systemui.statusbar.policy.KeyguardMonitor;
39import com.android.systemui.statusbar.policy.NetworkController;
40import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
41
42/**
43 * Contains the collapsed status bar and handles hiding/showing based on disable flags
44 * and keyguard state. Also manages lifecycle to make sure the views it contains are being
45 * updated by the StatusBarIconController and DarkIconManager while it is attached.
46 */
47public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks {
48
49    public static final String TAG = "CollapsedStatusBarFragment";
50    private static final String EXTRA_PANEL_STATE = "panel_state";
51    private PhoneStatusBarView mStatusBar;
52    private KeyguardMonitor mKeyguardMonitor;
53    private NetworkController mNetworkController;
54    private LinearLayout mSystemIconArea;
55    private View mNotificationIconAreaInner;
56    private int mDisabled1;
57    private StatusBar mStatusBarComponent;
58    private DarkIconManager mDarkIconManager;
59    private SignalClusterView mSignalClusterView;
60
61    private SignalCallback mSignalCallback = new SignalCallback() {
62        @Override
63        public void setIsAirplaneMode(NetworkController.IconState icon) {
64            mStatusBarComponent.recomputeDisableFlags(true /* animate */);
65        }
66    };
67
68    @Override
69    public void onCreate(@Nullable Bundle savedInstanceState) {
70        super.onCreate(savedInstanceState);
71        mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
72        mNetworkController = Dependency.get(NetworkController.class);
73        mStatusBarComponent = SysUiServiceProvider.getComponent(getContext(), StatusBar.class);
74    }
75
76    @Override
77    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
78            Bundle savedInstanceState) {
79        return inflater.inflate(R.layout.status_bar, container, false);
80    }
81
82    @Override
83    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
84        super.onViewCreated(view, savedInstanceState);
85        mStatusBar = (PhoneStatusBarView) view;
86        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
87            mStatusBar.go(savedInstanceState.getInt(EXTRA_PANEL_STATE));
88        }
89        mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
90        Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
91        mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
92        mSignalClusterView = mStatusBar.findViewById(R.id.signal_cluster);
93        Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mSignalClusterView);
94        // Default to showing until we know otherwise.
95        showSystemIconArea(false);
96        initEmergencyCryptkeeperText();
97    }
98
99    @Override
100    public void onSaveInstanceState(Bundle outState) {
101        super.onSaveInstanceState(outState);
102        outState.putInt(EXTRA_PANEL_STATE, mStatusBar.getState());
103    }
104
105    @Override
106    public void onResume() {
107        super.onResume();
108        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
109    }
110
111    @Override
112    public void onPause() {
113        super.onPause();
114        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
115    }
116
117    @Override
118    public void onDestroyView() {
119        super.onDestroyView();
120        Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mSignalClusterView);
121        Dependency.get(StatusBarIconController.class).removeIconGroup(mDarkIconManager);
122        if (mNetworkController.hasEmergencyCryptKeeperText()) {
123            mNetworkController.removeCallback(mSignalCallback);
124        }
125    }
126
127    public void initNotificationIconArea(NotificationIconAreaController
128            notificationIconAreaController) {
129        ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
130        mNotificationIconAreaInner =
131                notificationIconAreaController.getNotificationInnerAreaView();
132        if (mNotificationIconAreaInner.getParent() != null) {
133            ((ViewGroup) mNotificationIconAreaInner.getParent())
134                    .removeView(mNotificationIconAreaInner);
135        }
136        notificationIconArea.addView(mNotificationIconAreaInner);
137        // Default to showing until we know otherwise.
138        showNotificationIconArea(false);
139    }
140
141    @Override
142    public void disable(int state1, int state2, boolean animate) {
143        state1 = adjustDisableFlags(state1);
144        final int old1 = mDisabled1;
145        final int diff1 = state1 ^ old1;
146        mDisabled1 = state1;
147        if ((diff1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
148            if ((state1 & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
149                hideSystemIconArea(animate);
150            } else {
151                showSystemIconArea(animate);
152            }
153        }
154        if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
155            if ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
156                hideNotificationIconArea(animate);
157            } else {
158                showNotificationIconArea(animate);
159            }
160        }
161    }
162
163    protected int adjustDisableFlags(int state) {
164        if (!mStatusBarComponent.isLaunchTransitionFadingAway()
165                && !mKeyguardMonitor.isKeyguardFadingAway()
166                && shouldHideNotificationIcons()) {
167            state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
168            state |= StatusBarManager.DISABLE_SYSTEM_INFO;
169        }
170        if (mNetworkController != null && EncryptionHelper.IS_DATA_ENCRYPTED) {
171            if (mNetworkController.hasEmergencyCryptKeeperText()) {
172                state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
173            }
174            if (!mNetworkController.isRadioOn()) {
175                state |= StatusBarManager.DISABLE_SYSTEM_INFO;
176            }
177        }
178        return state;
179    }
180
181    private boolean shouldHideNotificationIcons() {
182        return !mStatusBar.isClosed() && mStatusBarComponent.hideStatusBarIconsWhenExpanded();
183    }
184
185    public void hideSystemIconArea(boolean animate) {
186        animateHide(mSystemIconArea, animate);
187    }
188
189    public void showSystemIconArea(boolean animate) {
190        animateShow(mSystemIconArea, animate);
191    }
192
193    public void hideNotificationIconArea(boolean animate) {
194        animateHide(mNotificationIconAreaInner, animate);
195    }
196
197    public void showNotificationIconArea(boolean animate) {
198        animateShow(mNotificationIconAreaInner, animate);
199    }
200
201    /**
202     * Hides a view.
203     */
204    private void animateHide(final View v, boolean animate) {
205        v.animate().cancel();
206        if (!animate) {
207            v.setAlpha(0f);
208            v.setVisibility(View.INVISIBLE);
209            return;
210        }
211        v.animate()
212                .alpha(0f)
213                .setDuration(160)
214                .setStartDelay(0)
215                .setInterpolator(Interpolators.ALPHA_OUT)
216                .withEndAction(() -> v.setVisibility(View.INVISIBLE));
217    }
218
219    /**
220     * Shows a view, and synchronizes the animation with Keyguard exit animations, if applicable.
221     */
222    private void animateShow(View v, boolean animate) {
223        v.animate().cancel();
224        v.setVisibility(View.VISIBLE);
225        if (!animate) {
226            v.setAlpha(1f);
227            return;
228        }
229        v.animate()
230                .alpha(1f)
231                .setDuration(320)
232                .setInterpolator(Interpolators.ALPHA_IN)
233                .setStartDelay(50)
234
235                // We need to clean up any pending end action from animateHide if we call
236                // both hide and show in the same frame before the animation actually gets started.
237                // cancel() doesn't really remove the end action.
238                .withEndAction(null);
239
240        // Synchronize the motion with the Keyguard fading if necessary.
241        if (mKeyguardMonitor.isKeyguardFadingAway()) {
242            v.animate()
243                    .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration())
244                    .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
245                    .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay())
246                    .start();
247        }
248    }
249
250    private void initEmergencyCryptkeeperText() {
251        View emergencyViewStub = mStatusBar.findViewById(R.id.emergency_cryptkeeper_text);
252        if (mNetworkController.hasEmergencyCryptKeeperText()) {
253            if (emergencyViewStub != null) {
254                ((ViewStub) emergencyViewStub).inflate();
255            }
256            mNetworkController.addCallback(mSignalCallback);
257        } else if (emergencyViewStub != null) {
258            ViewGroup parent = (ViewGroup) emergencyViewStub.getParent();
259            parent.removeView(emergencyViewStub);
260        }
261    }
262}
263