QuickStatusBarHeader.java revision 2f3551b04d5a5d05ba1eb7401f19c034708a9d00
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 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) mSettingsButton.getBackground()).setForceSoftware(true); 128 129 updateResources(); 130 } 131 132 @Override 133 protected void onConfigurationChanged(Configuration newConfig) { 134 super.onConfigurationChanged(newConfig); 135 updateResources(); 136 } 137 138 @Override 139 public void onRtlPropertiesChanged(int layoutDirection) { 140 super.onRtlPropertiesChanged(layoutDirection); 141 updateResources(); 142 } 143 144 private void updateResources() { 145 FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size); 146 FontSizeUtils.updateFontSize(mEmergencyOnly, R.dimen.qs_emergency_calls_only_text_size); 147 148 mGearTranslation = mContext.getResources().getDimension(R.dimen.qs_header_gear_translation); 149 150 mDateTimeTranslation = mContext.getResources().getDimension( 151 R.dimen.qs_date_anim_translation); 152 mDateTimeAlarmTranslation = mContext.getResources().getDimension( 153 R.dimen.qs_date_alarm_anim_translation); 154 float dateCollapsedSize = mContext.getResources().getDimension( 155 R.dimen.qs_date_collapsed_text_size); 156 float dateExpandedSize = mContext.getResources().getDimension( 157 R.dimen.qs_date_text_size); 158 mDateScaleFactor = dateExpandedSize / dateCollapsedSize; 159 updateDateTimePosition(); 160 161 mSecondHalfAnimator = new TouchAnimator.Builder() 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 176 updateSettingsAnimator(); 177 } 178 179 protected void updateSettingsAnimator() { 180 mSettingsAlpha = new TouchAnimator.Builder() 181 .addFloat(mSettingsContainer, "translationY", -mGearTranslation, 0) 182 .addFloat(mMultiUserSwitch, "translationY", -mGearTranslation, 0) 183 .addFloat(mSettingsButton, "rotation", -90, 0) 184 .addFloat(mSettingsContainer, "alpha", 0, 1) 185 .addFloat(mMultiUserSwitch, "alpha", 0, 1) 186 .setStartDelay(QSAnimator.EXPANDED_TILE_DELAY) 187 .build(); 188 189 final boolean isRtl = isLayoutRtl(); 190 if (isRtl && mDateTimeGroup.getWidth() == 0) { 191 mDateTimeGroup.addOnLayoutChangeListener(new OnLayoutChangeListener() { 192 @Override 193 public void onLayoutChange(View v, int left, int top, int right, int bottom, 194 int oldLeft, int oldTop, int oldRight, int oldBottom) { 195 mDateTimeGroup.setPivotX(getWidth()); 196 mDateTimeGroup.removeOnLayoutChangeListener(this); 197 } 198 }); 199 } else { 200 mDateTimeGroup.setPivotX(isRtl ? mDateTimeGroup.getWidth() : 0); 201 } 202 } 203 204 @Override 205 public int getCollapsedHeight() { 206 return getHeight(); 207 } 208 209 @Override 210 public int getExpandedHeight() { 211 return getHeight(); 212 } 213 214 @Override 215 public void setExpanded(boolean expanded) { 216 mExpanded = expanded; 217 updateEverything(); 218 } 219 220 @Override 221 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) { 222 mNextAlarm = nextAlarm; 223 if (nextAlarm != null) { 224 mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm)); 225 } 226 if (mAlarmShowing != (nextAlarm != null)) { 227 mAlarmShowing = nextAlarm != null; 228 updateEverything(); 229 } 230 } 231 232 @Override 233 public void setExpansion(float headerExpansionFraction) { 234 mExpansionAmount = headerExpansionFraction; 235 mSecondHalfAnimator.setPosition(headerExpansionFraction); 236 mFirstHalfAnimator.setPosition(headerExpansionFraction); 237 mDateSizeAnimator.setPosition(headerExpansionFraction); 238 mAlarmTranslation.setPosition(headerExpansionFraction); 239 mSettingsAlpha.setPosition(headerExpansionFraction); 240 241 updateAlarmVisibilities(); 242 243 mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD); 244 } 245 246 @Override 247 protected void onDetachedFromWindow() { 248 setListening(false); 249 mHost.getUserInfoController().remListener(this); 250 mHost.getNetworkController().removeEmergencyListener(this); 251 super.onDetachedFromWindow(); 252 } 253 254 private void updateAlarmVisibilities() { 255 mAlarmStatus.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE); 256 mAlarmStatusCollapsed.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE); 257 } 258 259 private void updateDateTimePosition() { 260 // This one has its own because we have to rebuild it every time the alarm state changes. 261 mAlarmTranslation = new TouchAnimator.Builder() 262 .addFloat(mDateTimeAlarmGroup, "translationY", 0, mAlarmShowing 263 ? mDateTimeAlarmTranslation : mDateTimeTranslation) 264 .build(); 265 mAlarmTranslation.setPosition(mExpansionAmount); 266 } 267 268 public void setListening(boolean listening) { 269 if (listening == mListening) { 270 return; 271 } 272 mHeaderQsPanel.setListening(listening); 273 mListening = listening; 274 updateListeners(); 275 } 276 277 @Override 278 public void updateEverything() { 279 updateDateTimePosition(); 280 updateVisibilities(); 281 } 282 283 protected void updateVisibilities() { 284 updateAlarmVisibilities(); 285 mEmergencyOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly 286 ? View.VISIBLE : View.INVISIBLE); 287 mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility( 288 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE); 289 mMultiUserSwitch.setVisibility(mMultiUserSwitch.hasMultipleUsers() ? View.VISIBLE 290 : View.GONE); 291 } 292 293 private void updateListeners() { 294 if (mListening) { 295 mNextAlarmController.addStateChangedCallback(this); 296 } else { 297 mNextAlarmController.removeStateChangedCallback(this); 298 } 299 } 300 301 @Override 302 public void setActivityStarter(ActivityStarter activityStarter) { 303 mActivityStarter = activityStarter; 304 } 305 306 @Override 307 public void setQSPanel(final QSPanel qsPanel) { 308 mQsPanel = qsPanel; 309 setupHost(qsPanel.getHost()); 310 if (mQsPanel != null) { 311 mMultiUserSwitch.setQsPanel(qsPanel); 312 } 313 } 314 315 public void setupHost(final QSTileHost host) { 316 mHost = host; 317 host.setHeaderView(mExpandIndicator); 318 mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this); 319 mHeaderQsPanel.setHost(host, null /* No customization in header */); 320 setUserInfoController(host.getUserInfoController()); 321 setBatteryController(host.getBatteryController()); 322 setNextAlarmController(host.getNextAlarmController()); 323 324 final boolean isAPhone = mHost.getNetworkController().hasVoiceCallingFeature(); 325 if (isAPhone) { 326 mHost.getNetworkController().addEmergencyListener(this); 327 } 328 } 329 330 @Override 331 public void onClick(View v) { 332 if (v == mSettingsButton) { 333 if (mSettingsButton.isTunerClick()) { 334 if (TunerService.isTunerEnabled(mContext)) { 335 TunerService.showResetRequest(mContext, new Runnable() { 336 @Override 337 public void run() { 338 // Relaunch settings so that the tuner disappears. 339 startSettingsActivity(); 340 } 341 }); 342 } else { 343 Toast.makeText(getContext(), R.string.tuner_toast, Toast.LENGTH_LONG).show(); 344 TunerService.setTunerEnabled(mContext, true); 345 } 346 } 347 startSettingsActivity(); 348 } else if (v == mAlarmStatus && mNextAlarm != null) { 349 PendingIntent showIntent = mNextAlarm.getShowIntent(); 350 if (showIntent != null && showIntent.isActivity()) { 351 mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */); 352 } 353 } 354 } 355 356 private void startSettingsActivity() { 357 mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS), 358 true /* dismissShade */); 359 } 360 361 @Override 362 public void setNextAlarmController(NextAlarmController nextAlarmController) { 363 mNextAlarmController = nextAlarmController; 364 } 365 366 @Override 367 public void setBatteryController(BatteryController batteryController) { 368 // Don't care 369 } 370 371 @Override 372 public void setUserInfoController(UserInfoController userInfoController) { 373 userInfoController.addListener(this); 374 } 375 376 @Override 377 public void setEmergencyCallsOnly(boolean show) { 378 boolean changed = show != mShowEmergencyCallsOnly; 379 if (changed) { 380 mShowEmergencyCallsOnly = show; 381 if (mExpanded) { 382 updateEverything(); 383 } 384 } 385 } 386 387 @Override 388 public void onUserInfoChanged(String name, Drawable picture) { 389 mMultiUserAvatar.setImageDrawable(picture); 390 } 391} 392