QuickStatusBarHeader.java revision 69e76ccee78cd20b1ee237cdd5a6134a276dd3e4
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar.phone;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.content.Context;
22import android.content.Intent;
23import android.graphics.Rect;
24import android.graphics.drawable.Animatable;
25import android.graphics.drawable.Drawable;
26import android.graphics.drawable.RippleDrawable;
27import android.util.AttributeSet;
28import android.view.View;
29import android.view.ViewGroup;
30import android.widget.ImageView;
31import android.widget.Switch;
32import android.widget.TextView;
33import android.widget.Toast;
34import com.android.keyguard.KeyguardStatusView;
35import com.android.systemui.R;
36import com.android.systemui.qs.QSPanel;
37import com.android.systemui.qs.QSTile;
38import com.android.systemui.qs.QuickQSPanel;
39import com.android.systemui.statusbar.policy.BatteryController;
40import com.android.systemui.statusbar.policy.NextAlarmController;
41import com.android.systemui.statusbar.policy.UserInfoController;
42import com.android.systemui.tuner.TunerService;
43
44public class QuickStatusBarHeader extends BaseStatusBarHeader implements
45        NextAlarmController.NextAlarmChangeCallback, View.OnClickListener {
46
47    private static final String TAG = "QuickStatusBarHeader";
48    private ActivityStarter mActivityStarter;
49    private NextAlarmController mNextAlarmController;
50    private SettingsButton mSettingsButton;
51    private View mSettingsContainer;
52    private TextView mAlarmStatus;
53    private View mQsDetailHeader;
54
55    private ImageView mQsDetailHeaderProgress;
56    private QSPanel mQsPanel;
57    private Switch mQsDetailHeaderSwitch;
58
59    private boolean mExpanded;
60    private boolean mAlarmShowing;
61    private boolean mShowingDetail;
62
63    private boolean mDetailTransitioning;
64    private ViewGroup mExpandedGroup;
65    private ViewGroup mDateTimeGroup;
66    private View mEmergencyOnly;
67    private TextView mQsDetailHeaderTitle;
68    private boolean mListening;
69    private AlarmManager.AlarmClockInfo mNextAlarm;
70
71    private QuickQSPanel mHeaderQsPanel;
72    private boolean mShowEmergencyCallsOnly;
73    private float mDateTimeTranslation;
74    private MultiUserSwitch mMultiUserSwitch;
75    private ImageView mMultiUserAvatar;
76
77    public QuickStatusBarHeader(Context context, AttributeSet attrs) {
78        super(context, attrs);
79    }
80
81    @Override
82    protected void onFinishInflate() {
83        super.onFinishInflate();
84
85        mEmergencyOnly = findViewById(R.id.header_emergency_calls_only);
86        mDateTimeTranslation = mContext.getResources().getDimension(
87                R.dimen.qs_date_anim_translation);
88        mDateTimeGroup = (ViewGroup) findViewById(R.id.date_time_group);
89        mExpandedGroup = (ViewGroup) findViewById(R.id.expanded_group);
90
91        mHeaderQsPanel = (QuickQSPanel) findViewById(R.id.quick_qs_panel);
92
93        mSettingsButton = (SettingsButton) findViewById(R.id.settings_button);
94        mSettingsContainer = findViewById(R.id.settings_button_container);
95        mSettingsButton.setOnClickListener(this);
96
97        mAlarmStatus = (TextView) findViewById(R.id.alarm_status);
98        mAlarmStatus.setOnClickListener(this);
99
100        mQsDetailHeader = findViewById(R.id.qs_detail_header);
101        mQsDetailHeader.setAlpha(0);
102        mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
103        mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
104        mQsDetailHeaderProgress = (ImageView) findViewById(R.id.qs_detail_header_progress);
105
106        mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
107        mMultiUserAvatar = (ImageView) mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
108
109        // RenderThread is doing more harm than good when touching the header (to expand quick
110        // settings), so disable it for this view
111        ((RippleDrawable) getBackground()).setForceSoftware(true);
112        ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
113
114        addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
115            @Override
116            public void onLayoutChange(View v, int left, int top, int right,
117                    int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
118                setClipBounds(new Rect(getPaddingLeft(), 0, getWidth() - getPaddingRight(),
119                        getHeight()));
120            }
121        });
122    }
123
124    @Override
125    public int getCollapsedHeight() {
126        return getHeight();
127    }
128
129    @Override
130    public int getExpandedHeight() {
131        return getHeight();
132    }
133
134    @Override
135    public void setExpanded(boolean expanded) {
136        mExpanded = expanded;
137        updateEverything();
138    }
139
140    @Override
141    public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
142        mNextAlarm = nextAlarm;
143        if (nextAlarm != null) {
144            mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
145        }
146        mAlarmShowing = nextAlarm != null;
147        updateEverything();
148    }
149
150    @Override
151    public void setExpansion(float headerExpansionFraction) {
152        mExpandedGroup.setAlpha(headerExpansionFraction);
153        mExpandedGroup.setVisibility(headerExpansionFraction > 0 ? View.VISIBLE : View.INVISIBLE);
154        mHeaderQsPanel.setAlpha(1 - headerExpansionFraction);
155        mHeaderQsPanel.setVisibility(headerExpansionFraction < 1 ? View.VISIBLE : View.INVISIBLE);
156
157        mDateTimeGroup.setTranslationY(headerExpansionFraction * mDateTimeTranslation);
158        mEmergencyOnly.setAlpha(headerExpansionFraction);
159    }
160
161    public void setListening(boolean listening) {
162        if (listening == mListening) {
163            return;
164        }
165        mHeaderQsPanel.setListening(listening);
166        mListening = listening;
167        updateListeners();
168    }
169
170    @Override
171    public void updateEverything() {
172        updateVisibilities();
173    }
174
175    private void updateVisibilities() {
176        mAlarmStatus.setVisibility(mAlarmShowing ? View.VISIBLE : View.GONE);
177        mQsDetailHeader.setVisibility(mExpanded && mShowingDetail ? View.VISIBLE : View.INVISIBLE);
178        mEmergencyOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly
179                ? View.VISIBLE : View.INVISIBLE);
180        mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
181                TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
182        mMultiUserSwitch.setVisibility(mMultiUserSwitch.hasMultipleUsers() ? View.VISIBLE
183                : View.GONE);
184    }
185
186    private boolean hasMultiUsers() {
187        return false;
188    }
189
190    private void updateListeners() {
191        if (mListening) {
192            mNextAlarmController.addStateChangedCallback(this);
193        } else {
194            mNextAlarmController.removeStateChangedCallback(this);
195        }
196    }
197
198    @Override
199    public void setActivityStarter(ActivityStarter activityStarter) {
200        mActivityStarter = activityStarter;
201    }
202
203    @Override
204    public void setQSPanel(final QSPanel qsPanel) {
205        mQsPanel = qsPanel;
206        setupHost(qsPanel.getHost());
207        if (mQsPanel != null) {
208            mQsPanel.setCallback(mQsPanelCallback);
209            mMultiUserSwitch.setQsPanel(qsPanel);
210        }
211    }
212
213    public void setupHost(final QSTileHost host) {
214        host.setHeaderView(this);
215        mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
216        mHeaderQsPanel.setHost(host);
217        mHeaderQsPanel.setMaxTiles(5);
218        mHeaderQsPanel.setTiles(host.getTiles());
219        host.addCallback(new QSTile.Host.Callback() {
220            @Override
221            public void onTilesChanged() {
222                mHeaderQsPanel.setTiles(host.getTiles());
223            }
224        });
225    }
226
227    @Override
228    public void onClick(View v) {
229        if (v == mSettingsButton) {
230            if (mSettingsButton.isTunerClick()) {
231                if (TunerService.isTunerEnabled(mContext)) {
232                    TunerService.showResetRequest(mContext, new Runnable() {
233                        @Override
234                        public void run() {
235                            // Relaunch settings so that the tuner disappears.
236                            startSettingsActivity();
237                        }
238                    });
239                } else {
240                    Toast.makeText(getContext(), R.string.tuner_toast, Toast.LENGTH_LONG).show();
241                    TunerService.setTunerEnabled(mContext, true);
242                }
243            }
244            startSettingsActivity();
245        } else if (v == mAlarmStatus && mNextAlarm != null) {
246            PendingIntent showIntent = mNextAlarm.getShowIntent();
247            if (showIntent != null && showIntent.isActivity()) {
248                mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */);
249            }
250        }
251    }
252
253    private void startSettingsActivity() {
254        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
255                true /* dismissShade */);
256    }
257
258    @Override
259    public void setNextAlarmController(NextAlarmController nextAlarmController) {
260        mNextAlarmController = nextAlarmController;
261    }
262
263    @Override
264    public void setBatteryController(BatteryController batteryController) {
265        // Don't care
266    }
267
268    @Override
269    public void setUserInfoController(UserInfoController userInfoController) {
270        userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
271            @Override
272            public void onUserInfoChanged(String name, Drawable picture) {
273                mMultiUserAvatar.setImageDrawable(picture);
274            }
275        });
276    }
277
278    @Override
279    public void setEmergencyCallsOnly(boolean show) {
280        boolean changed = show != mShowEmergencyCallsOnly;
281        if (changed) {
282            mShowEmergencyCallsOnly = show;
283            if (mExpanded) {
284                updateEverything();
285            }
286        }
287    }
288
289    private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
290        private boolean mScanState;
291
292        @Override
293        public void onShowingDetail(final QSTile.DetailAdapter detail) {
294            mDetailTransitioning = true;
295            post(new Runnable() {
296                @Override
297                public void run() {
298                    handleShowingDetail(detail);
299                }
300            });
301        }
302
303        @Override
304        public void onToggleStateChanged(final boolean state) {
305            post(new Runnable() {
306                @Override
307                public void run() {
308                    handleToggleStateChanged(state);
309                }
310            });
311
312        }
313
314        @Override
315        public void onScanStateChanged(final boolean state) {
316            post(new Runnable() {
317                @Override
318                public void run() {
319                    handleScanStateChanged(state);
320                }
321            });
322        }
323
324        private void handleToggleStateChanged(boolean state) {
325            mQsDetailHeaderSwitch.setChecked(state);
326        }
327
328        private void handleScanStateChanged(boolean state) {
329            if (mScanState == state) return;
330            mScanState = state;
331            final Animatable anim = (Animatable) mQsDetailHeaderProgress.getDrawable();
332            if (state) {
333                mQsDetailHeaderProgress.animate().alpha(1f);
334                anim.start();
335            } else {
336                mQsDetailHeaderProgress.animate().alpha(0f);
337                anim.stop();
338            }
339        }
340
341        private void handleShowingDetail(final QSTile.DetailAdapter detail) {
342            final boolean showingDetail = detail != null;
343            transition(mDateTimeGroup, !showingDetail);
344            transition(mExpandedGroup, !showingDetail);
345            if (mAlarmShowing) {
346                transition(mAlarmStatus, !showingDetail);
347            }
348            transition(mQsDetailHeader, showingDetail);
349            mShowingDetail = showingDetail;
350            if (showingDetail) {
351                mQsDetailHeaderTitle.setText(detail.getTitle());
352                final Boolean toggleState = detail.getToggleState();
353                if (toggleState == null) {
354                    mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
355                    mQsDetailHeader.setClickable(false);
356                } else {
357                    mQsDetailHeaderSwitch.setVisibility(VISIBLE);
358                    mQsDetailHeaderSwitch.setChecked(toggleState);
359                    mQsDetailHeader.setClickable(true);
360                    mQsDetailHeader.setOnClickListener(new OnClickListener() {
361                        @Override
362                        public void onClick(View v) {
363                            boolean checked = !mQsDetailHeaderSwitch.isChecked();
364                            mQsDetailHeaderSwitch.setChecked(checked);
365                            detail.setToggleState(checked);
366                        }
367                    });
368                }
369            } else {
370                mQsDetailHeader.setClickable(false);
371            }
372        }
373
374        private void transition(final View v, final boolean in) {
375            if (in) {
376                v.bringToFront();
377                v.setVisibility(VISIBLE);
378            }
379            if (v.hasOverlappingRendering()) {
380                v.animate().withLayer();
381            }
382            v.animate()
383                    .alpha(in ? 1 : 0)
384                    .withEndAction(new Runnable() {
385                        @Override
386                        public void run() {
387                            if (!in) {
388                                v.setVisibility(INVISIBLE);
389                            }
390                            mDetailTransitioning = false;
391                        }
392                    })
393                    .start();
394        }
395    };
396}
397