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