QuickStatusBarHeader.java revision 50805412c8de7ccac7fa63aa87283fdf6a35c812
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.content.res.Configuration;
24import android.graphics.drawable.Drawable;
25import android.graphics.drawable.RippleDrawable;
26import android.os.UserManager;
27import android.util.AttributeSet;
28import android.view.View;
29import android.view.View.OnClickListener;
30import android.view.ViewGroup;
31import android.widget.ImageView;
32import android.widget.TextView;
33import android.widget.Toast;
34
35import com.android.internal.logging.MetricsLogger;
36import com.android.internal.logging.MetricsProto;
37import com.android.keyguard.KeyguardStatusView;
38import com.android.systemui.FontSizeUtils;
39import com.android.systemui.R;
40import com.android.systemui.qs.QSPanel;
41import com.android.systemui.qs.QSPanel.Callback;
42import com.android.systemui.qs.QuickQSPanel;
43import com.android.systemui.qs.TouchAnimator;
44import com.android.systemui.qs.TouchAnimator.Builder;
45import com.android.systemui.statusbar.policy.BatteryController;
46import com.android.systemui.statusbar.policy.NextAlarmController;
47import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
48import com.android.systemui.statusbar.policy.UserInfoController;
49import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
50import com.android.systemui.tuner.TunerService;
51
52public class QuickStatusBarHeader extends BaseStatusBarHeader implements
53        NextAlarmChangeCallback, OnClickListener, OnUserInfoChangedListener {
54
55    private static final String TAG = "QuickStatusBarHeader";
56
57    private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
58
59    private ActivityStarter mActivityStarter;
60    private NextAlarmController mNextAlarmController;
61    private SettingsButton mSettingsButton;
62    protected View mSettingsContainer;
63
64    private TextView mAlarmStatus;
65    private View mAlarmStatusCollapsed;
66
67    private QSPanel mQsPanel;
68
69    private boolean mExpanded;
70    private boolean mAlarmShowing;
71
72    private ViewGroup mDateTimeGroup;
73    private ViewGroup mDateTimeAlarmGroup;
74    private TextView mEmergencyOnly;
75
76    protected ExpandableIndicator mExpandIndicator;
77
78    private boolean mListening;
79    private AlarmManager.AlarmClockInfo mNextAlarm;
80
81    private QuickQSPanel mHeaderQsPanel;
82    private boolean mShowEmergencyCallsOnly;
83    protected MultiUserSwitch mMultiUserSwitch;
84    private ImageView mMultiUserAvatar;
85
86
87    private TouchAnimator mAnimator;
88    protected TouchAnimator mSettingsAlpha;
89    private float mExpansionAmount;
90    private QSTileHost mHost;
91    private View mEdit;
92    private boolean mShowFullAlarm;
93    private float mDateTimeTranslation;
94
95    public QuickStatusBarHeader(Context context, AttributeSet attrs) {
96        super(context, attrs);
97    }
98
99    @Override
100    protected void onFinishInflate() {
101        super.onFinishInflate();
102
103        mEmergencyOnly = (TextView) findViewById(R.id.header_emergency_calls_only);
104
105        mEdit = findViewById(android.R.id.edit);
106        findViewById(android.R.id.edit).setOnClickListener(view ->
107                mHost.startRunnableDismissingKeyguard(() -> mQsPanel.showEdit(view)));
108
109        mDateTimeAlarmGroup = (ViewGroup) findViewById(R.id.date_time_alarm_group);
110        mDateTimeAlarmGroup.findViewById(R.id.empty_time_view).setVisibility(View.GONE);
111        mDateTimeGroup = (ViewGroup) findViewById(R.id.date_time_group);
112        mDateTimeGroup.setPivotX(0);
113        mDateTimeGroup.setPivotY(0);
114        mDateTimeTranslation = getResources().getDimension(R.dimen.qs_date_time_translation);
115        mShowFullAlarm = getResources().getBoolean(R.bool.quick_settings_show_full_alarm);
116
117        mExpandIndicator = (ExpandableIndicator) findViewById(R.id.expand_indicator);
118
119        mHeaderQsPanel = (QuickQSPanel) findViewById(R.id.quick_qs_panel);
120
121        mSettingsButton = (SettingsButton) findViewById(R.id.settings_button);
122        mSettingsContainer = findViewById(R.id.settings_button_container);
123        mSettingsButton.setOnClickListener(this);
124
125        mAlarmStatusCollapsed = findViewById(R.id.alarm_status_collapsed);
126        mAlarmStatus = (TextView) findViewById(R.id.alarm_status);
127        mAlarmStatus.setOnClickListener(this);
128
129        mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
130        mMultiUserAvatar = (ImageView) mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
131
132        // RenderThread is doing more harm than good when touching the header (to expand quick
133        // settings), so disable it for this view
134        ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
135        ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
136
137        updateResources();
138    }
139
140    @Override
141    protected void onConfigurationChanged(Configuration newConfig) {
142        super.onConfigurationChanged(newConfig);
143        updateResources();
144    }
145
146    @Override
147    public void onRtlPropertiesChanged(int layoutDirection) {
148        super.onRtlPropertiesChanged(layoutDirection);
149        updateResources();
150    }
151
152    private void updateResources() {
153        FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size);
154        FontSizeUtils.updateFontSize(mEmergencyOnly, R.dimen.qs_emergency_calls_only_text_size);
155
156        Builder builder = new Builder()
157                .addFloat(mShowFullAlarm ? mAlarmStatus : findViewById(R.id.date), "alpha", 0, 1)
158                .addFloat(mEmergencyOnly, "alpha", 0, 1);
159        if (mShowFullAlarm) {
160            builder.addFloat(mAlarmStatusCollapsed, "alpha", 1, 0);
161        }
162        mAnimator = builder.build();
163
164        updateSettingsAnimator();
165    }
166
167    protected void updateSettingsAnimator() {
168        mSettingsAlpha = new TouchAnimator.Builder()
169                .addFloat(mEdit, "alpha", 0, 1)
170                .addFloat(mMultiUserSwitch, "alpha", 0, 1)
171                .build();
172
173        final boolean isRtl = isLayoutRtl();
174        if (isRtl && mDateTimeGroup.getWidth() == 0) {
175            mDateTimeGroup.addOnLayoutChangeListener(new OnLayoutChangeListener() {
176                @Override
177                public void onLayoutChange(View v, int left, int top, int right, int bottom,
178                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
179                    mDateTimeGroup.setPivotX(getWidth());
180                    mDateTimeGroup.removeOnLayoutChangeListener(this);
181                }
182            });
183        } else {
184            mDateTimeGroup.setPivotX(isRtl ? mDateTimeGroup.getWidth() : 0);
185        }
186    }
187
188    @Override
189    public int getCollapsedHeight() {
190        return getHeight();
191    }
192
193    @Override
194    public int getExpandedHeight() {
195        return getHeight();
196    }
197
198    @Override
199    public void setExpanded(boolean expanded) {
200        mExpanded = expanded;
201        mHeaderQsPanel.setExpanded(expanded);
202        updateEverything();
203    }
204
205    @Override
206    public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
207        mNextAlarm = nextAlarm;
208        if (nextAlarm != null) {
209            String alarmString = KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm);
210            mAlarmStatus.setText(alarmString);
211            mAlarmStatus.setContentDescription(mContext.getString(
212                    R.string.accessibility_quick_settings_alarm, alarmString));
213            mAlarmStatusCollapsed.setContentDescription(mContext.getString(
214                    R.string.accessibility_quick_settings_alarm, alarmString));
215        }
216        if (mAlarmShowing != (nextAlarm != null)) {
217            mAlarmShowing = nextAlarm != null;
218            updateEverything();
219        }
220    }
221
222    @Override
223    public void setExpansion(float headerExpansionFraction) {
224        mExpansionAmount = headerExpansionFraction;
225        updateDateTimePosition();
226        mAnimator.setPosition(headerExpansionFraction);
227        mSettingsAlpha.setPosition(headerExpansionFraction);
228
229        updateAlarmVisibilities();
230
231        mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
232    }
233
234    @Override
235    protected void onDetachedFromWindow() {
236        setListening(false);
237        mHost.getUserInfoController().remListener(this);
238        mHost.getNetworkController().removeEmergencyListener(this);
239        super.onDetachedFromWindow();
240    }
241
242    private void updateAlarmVisibilities() {
243        mAlarmStatus.setVisibility(mAlarmShowing && mShowFullAlarm ? View.VISIBLE : View.INVISIBLE);
244        mAlarmStatusCollapsed.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
245    }
246
247    public void setListening(boolean listening) {
248        if (listening == mListening) {
249            return;
250        }
251        mHeaderQsPanel.setListening(listening);
252        mListening = listening;
253        updateListeners();
254    }
255
256    @Override
257    public void updateEverything() {
258        updateVisibilities();
259        setClickable(false);
260    }
261
262    protected void updateVisibilities() {
263        updateAlarmVisibilities();
264        updateDateTimePosition();
265        mEmergencyOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly
266                ? View.VISIBLE : View.INVISIBLE);
267        mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
268                TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
269        final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
270        mMultiUserSwitch.setVisibility(mExpanded && mMultiUserSwitch.hasMultipleUsers() && !isDemo
271                ? View.VISIBLE : View.INVISIBLE);
272        mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
273    }
274
275    private void updateDateTimePosition() {
276        mDateTimeAlarmGroup.setTranslationY(mShowEmergencyCallsOnly
277                ? mExpansionAmount * mDateTimeTranslation : 0);
278    }
279
280    private void updateListeners() {
281        if (mListening) {
282            mNextAlarmController.addStateChangedCallback(this);
283        } else {
284            mNextAlarmController.removeStateChangedCallback(this);
285        }
286    }
287
288    @Override
289    public void setActivityStarter(ActivityStarter activityStarter) {
290        mActivityStarter = activityStarter;
291    }
292
293    @Override
294    public void setQSPanel(final QSPanel qsPanel) {
295        mQsPanel = qsPanel;
296        setupHost(qsPanel.getHost());
297        if (mQsPanel != null) {
298            mMultiUserSwitch.setQsPanel(qsPanel);
299        }
300    }
301
302    public void setupHost(final QSTileHost host) {
303        mHost = host;
304        host.setHeaderView(mExpandIndicator);
305        mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
306        mHeaderQsPanel.setHost(host, null /* No customization in header */);
307        setUserInfoController(host.getUserInfoController());
308        setBatteryController(host.getBatteryController());
309        setNextAlarmController(host.getNextAlarmController());
310
311        final boolean isAPhone = mHost.getNetworkController().hasVoiceCallingFeature();
312        if (isAPhone) {
313            mHost.getNetworkController().addEmergencyListener(this);
314        }
315    }
316
317    @Override
318    public void onClick(View v) {
319        if (v == mSettingsButton) {
320            MetricsLogger.action(mContext,
321                    mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
322                            : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
323            if (mSettingsButton.isTunerClick()) {
324                mHost.startRunnableDismissingKeyguard(() -> post(() -> {
325                    if (TunerService.isTunerEnabled(mContext)) {
326                        TunerService.showResetRequest(mContext, () -> {
327                            // Relaunch settings so that the tuner disappears.
328                            startSettingsActivity();
329                        });
330                    } else {
331                        Toast.makeText(getContext(), R.string.tuner_toast,
332                                Toast.LENGTH_LONG).show();
333                        TunerService.setTunerEnabled(mContext, true);
334                    }
335                    startSettingsActivity();
336
337                }));
338            } else {
339                startSettingsActivity();
340            }
341        } else if (v == mAlarmStatus && mNextAlarm != null) {
342            PendingIntent showIntent = mNextAlarm.getShowIntent();
343            if (showIntent != null && showIntent.isActivity()) {
344                mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */);
345            }
346        }
347    }
348
349    private void startSettingsActivity() {
350        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
351                true /* dismissShade */);
352    }
353
354    @Override
355    public void setNextAlarmController(NextAlarmController nextAlarmController) {
356        mNextAlarmController = nextAlarmController;
357    }
358
359    @Override
360    public void setBatteryController(BatteryController batteryController) {
361        // Don't care
362    }
363
364    @Override
365    public void setUserInfoController(UserInfoController userInfoController) {
366        userInfoController.addListener(this);
367    }
368
369    @Override
370    public void setCallback(Callback qsPanelCallback) {
371        mHeaderQsPanel.setCallback(qsPanelCallback);
372    }
373
374    @Override
375    public void setEmergencyCallsOnly(boolean show) {
376        boolean changed = show != mShowEmergencyCallsOnly;
377        if (changed) {
378            mShowEmergencyCallsOnly = show;
379            if (mExpanded) {
380                updateEverything();
381            }
382        }
383    }
384
385    @Override
386    public void onUserInfoChanged(String name, Drawable picture) {
387        mMultiUserAvatar.setImageDrawable(picture);
388    }
389}
390