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