QuickStatusBarHeader.java revision 8b9d67fb636a89fe6537e7affb9d01a75f263d93
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 {
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    private 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    private ExpandableIndicator mExpandIndicator;
72
73    private boolean mListening;
74    private AlarmManager.AlarmClockInfo mNextAlarm;
75
76    private QuickQSPanel mHeaderQsPanel;
77    private boolean mShowEmergencyCallsOnly;
78    private MultiUserSwitch mMultiUserSwitch;
79    private ImageView mMultiUserAvatar;
80
81    private float mDateTimeTranslation;
82    private float mDateTimeAlarmTranslation;
83    private float mDateScaleFactor;
84    private float mGearTranslation;
85
86    private TouchAnimator mAnimator;
87    private TouchAnimator mSecondHalfAnimator;
88    private TouchAnimator mFirstHalfAnimator;
89    private TouchAnimator mDateSizeAnimator;
90    private TouchAnimator mAlarmTranslation;
91    private TouchAnimator mSettingsAlpha;
92    private float mExpansionAmount;
93    private QSTileHost mHost;
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        mDateTimeAlarmGroup = (ViewGroup) findViewById(R.id.date_time_alarm_group);
106        mDateTimeAlarmGroup.findViewById(R.id.empty_time_view).setVisibility(View.GONE);
107        mDateTimeGroup = (ViewGroup) findViewById(R.id.date_time_group);
108        mDateTimeGroup.setPivotX(0);
109        mDateTimeGroup.setPivotY(0);
110
111        mExpandIndicator = (ExpandableIndicator) findViewById(R.id.expand_indicator);
112
113        mHeaderQsPanel = (QuickQSPanel) findViewById(R.id.quick_qs_panel);
114
115        mSettingsButton = (SettingsButton) findViewById(R.id.settings_button);
116        mSettingsContainer = findViewById(R.id.settings_button_container);
117        mSettingsButton.setOnClickListener(this);
118
119        mAlarmStatusCollapsed = (TextView) findViewById(R.id.alarm_status_collapsed);
120        mAlarmStatus = (TextView) findViewById(R.id.alarm_status);
121        mAlarmStatus.setOnClickListener(this);
122
123        mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
124        mMultiUserAvatar = (ImageView) mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
125
126        // RenderThread is doing more harm than good when touching the header (to expand quick
127        // settings), so disable it for this view
128        ((RippleDrawable) getBackground()).setForceSoftware(true);
129        ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
130
131        updateResources();
132    }
133
134    @Override
135    protected void onConfigurationChanged(Configuration newConfig) {
136        super.onConfigurationChanged(newConfig);
137        updateResources();
138    }
139
140    private void updateResources() {
141        FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size);
142        FontSizeUtils.updateFontSize(mEmergencyOnly, R.dimen.qs_emergency_calls_only_text_size);
143
144        mGearTranslation = mContext.getResources().getDimension(R.dimen.qs_header_gear_translation);
145
146        mDateTimeTranslation = mContext.getResources().getDimension(
147                R.dimen.qs_date_anim_translation);
148        mDateTimeAlarmTranslation = mContext.getResources().getDimension(
149                R.dimen.qs_date_alarm_anim_translation);
150        float dateCollapsedSize = mContext.getResources().getDimension(
151                R.dimen.qs_date_collapsed_text_size);
152        float dateExpandedSize = mContext.getResources().getDimension(
153                R.dimen.qs_date_text_size);
154        mDateScaleFactor = dateExpandedSize / dateCollapsedSize;
155        updateDateTimePosition();
156
157        mAnimator = new TouchAnimator.Builder()
158                .addFloat(mSettingsContainer, "translationY", -mGearTranslation, 0)
159                .addFloat(mMultiUserSwitch, "translationY", -mGearTranslation, 0)
160                .build();
161        mSecondHalfAnimator = new TouchAnimator.Builder()
162                .addFloat(mSettingsButton, "rotation", -180, 0)
163                .addFloat(mAlarmStatus, "alpha", 0, 1)
164                .addFloat(mEmergencyOnly, "alpha", 0, 1)
165                .setStartDelay(.5f)
166                .build();
167        mFirstHalfAnimator = new TouchAnimator.Builder()
168                .addFloat(mAlarmStatusCollapsed, "alpha", 1, 0)
169                .setEndDelay(.5f)
170                .build();
171        mDateSizeAnimator = new TouchAnimator.Builder()
172                .addFloat(mDateTimeGroup, "scaleX", 1, mDateScaleFactor)
173                .addFloat(mDateTimeGroup, "scaleY", 1, mDateScaleFactor)
174                .setStartDelay(.36f)
175                .build();
176        mSettingsAlpha = new TouchAnimator.Builder()
177                .addFloat(mSettingsContainer, "alpha", 0, 1)
178                .addFloat(mMultiUserSwitch, "alpha", 0, 1)
179                .setStartDelay(QSAnimator.EXPANDED_TILE_DELAY)
180                .build();
181    }
182
183    @Override
184    public int getCollapsedHeight() {
185        return getHeight();
186    }
187
188    @Override
189    public int getExpandedHeight() {
190        return getHeight();
191    }
192
193    @Override
194    public void setExpanded(boolean expanded) {
195        mExpanded = expanded;
196        updateEverything();
197    }
198
199    @Override
200    public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
201        mNextAlarm = nextAlarm;
202        if (nextAlarm != null) {
203            mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
204        }
205        if (mAlarmShowing != (nextAlarm != null)) {
206            mAlarmShowing = nextAlarm != null;
207            updateEverything();
208        }
209    }
210
211    @Override
212    public void setExpansion(float headerExpansionFraction) {
213        mExpansionAmount = headerExpansionFraction;
214        mAnimator.setPosition(headerExpansionFraction);
215        mSecondHalfAnimator.setPosition(headerExpansionFraction);
216        mFirstHalfAnimator.setPosition(headerExpansionFraction);
217        mDateSizeAnimator.setPosition(headerExpansionFraction);
218        mAlarmTranslation.setPosition(headerExpansionFraction);
219        mSettingsAlpha.setPosition(headerExpansionFraction);
220
221        updateAlarmVisibilities();
222
223        mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
224    }
225
226    @Override
227    protected void onDetachedFromWindow() {
228        setListening(false);
229        mHost.getUserInfoController().remListener(mUserListener);
230        mHost.getNetworkController().removeEmergencyListener(this);
231        super.onDetachedFromWindow();
232    }
233
234    private void updateAlarmVisibilities() {
235        mAlarmStatus.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
236        mAlarmStatusCollapsed.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
237    }
238
239    private void updateDateTimePosition() {
240        // This one has its own because we have to rebuild it every time the alarm state changes.
241        mAlarmTranslation = new TouchAnimator.Builder()
242                .addFloat(mDateTimeAlarmGroup, "translationY", 0, mAlarmShowing
243                        ? mDateTimeAlarmTranslation : mDateTimeTranslation)
244                .build();
245        mAlarmTranslation.setPosition(mExpansionAmount);
246    }
247
248    public void setListening(boolean listening) {
249        if (listening == mListening) {
250            return;
251        }
252        mHeaderQsPanel.setListening(listening);
253        mListening = listening;
254        updateListeners();
255    }
256
257    @Override
258    public void updateEverything() {
259        updateDateTimePosition();
260        updateVisibilities();
261    }
262
263    private void updateVisibilities() {
264        updateAlarmVisibilities();
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        mMultiUserSwitch.setVisibility(mMultiUserSwitch.hasMultipleUsers() ? View.VISIBLE
270                : View.GONE);
271    }
272
273    private void updateListeners() {
274        if (mListening) {
275            mNextAlarmController.addStateChangedCallback(this);
276        } else {
277            mNextAlarmController.removeStateChangedCallback(this);
278        }
279    }
280
281    @Override
282    public void setActivityStarter(ActivityStarter activityStarter) {
283        mActivityStarter = activityStarter;
284    }
285
286    @Override
287    public void setQSPanel(final QSPanel qsPanel) {
288        mQsPanel = qsPanel;
289        setupHost(qsPanel.getHost());
290        if (mQsPanel != null) {
291            mMultiUserSwitch.setQsPanel(qsPanel);
292        }
293    }
294
295    public void setupHost(final QSTileHost host) {
296        mHost = host;
297        host.setHeaderView(this);
298        mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
299        mHeaderQsPanel.setHost(host, null /* No customization in header */);
300        setUserInfoController(host.getUserInfoController());
301        setBatteryController(host.getBatteryController());
302        setNextAlarmController(host.getNextAlarmController());
303
304        final boolean isAPhone = mHost.getNetworkController().hasVoiceCallingFeature();
305        if (isAPhone) {
306            mHost.getNetworkController().addEmergencyListener(this);
307        }
308    }
309
310    @Override
311    public void onClick(View v) {
312        if (v == mSettingsButton) {
313            if (mSettingsButton.isTunerClick()) {
314                if (TunerService.isTunerEnabled(mContext)) {
315                    TunerService.showResetRequest(mContext, new Runnable() {
316                        @Override
317                        public void run() {
318                            // Relaunch settings so that the tuner disappears.
319                            startSettingsActivity();
320                        }
321                    });
322                } else {
323                    Toast.makeText(getContext(), R.string.tuner_toast, Toast.LENGTH_LONG).show();
324                    TunerService.setTunerEnabled(mContext, true);
325                }
326            }
327            startSettingsActivity();
328        } else if (v == mAlarmStatus && mNextAlarm != null) {
329            PendingIntent showIntent = mNextAlarm.getShowIntent();
330            if (showIntent != null && showIntent.isActivity()) {
331                mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */);
332            }
333        }
334    }
335
336    private void startSettingsActivity() {
337        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
338                true /* dismissShade */);
339    }
340
341    @Override
342    public void setNextAlarmController(NextAlarmController nextAlarmController) {
343        mNextAlarmController = nextAlarmController;
344    }
345
346    @Override
347    public void setBatteryController(BatteryController batteryController) {
348        // Don't care
349    }
350
351    @Override
352    public void setUserInfoController(UserInfoController userInfoController) {
353        userInfoController.addListener(mUserListener);
354    }
355
356    @Override
357    public void setEmergencyCallsOnly(boolean show) {
358        boolean changed = show != mShowEmergencyCallsOnly;
359        if (changed) {
360            mShowEmergencyCallsOnly = show;
361            if (mExpanded) {
362                updateEverything();
363            }
364        }
365    }
366
367    private final OnUserInfoChangedListener mUserListener = new OnUserInfoChangedListener() {
368        @Override
369        public void onUserInfoChanged(String name, Drawable picture) {
370            mMultiUserAvatar.setImageDrawable(picture);
371        }
372    };
373}
374