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