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