QuickStatusBarHeader.java revision 69e76ccee78cd20b1ee237cdd5a6134a276dd3e4
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.graphics.Rect; 24import android.graphics.drawable.Animatable; 25import android.graphics.drawable.Drawable; 26import android.graphics.drawable.RippleDrawable; 27import android.util.AttributeSet; 28import android.view.View; 29import android.view.ViewGroup; 30import android.widget.ImageView; 31import android.widget.Switch; 32import android.widget.TextView; 33import android.widget.Toast; 34import com.android.keyguard.KeyguardStatusView; 35import com.android.systemui.R; 36import com.android.systemui.qs.QSPanel; 37import com.android.systemui.qs.QSTile; 38import com.android.systemui.qs.QuickQSPanel; 39import com.android.systemui.statusbar.policy.BatteryController; 40import com.android.systemui.statusbar.policy.NextAlarmController; 41import com.android.systemui.statusbar.policy.UserInfoController; 42import com.android.systemui.tuner.TunerService; 43 44public class QuickStatusBarHeader extends BaseStatusBarHeader implements 45 NextAlarmController.NextAlarmChangeCallback, View.OnClickListener { 46 47 private static final String TAG = "QuickStatusBarHeader"; 48 private ActivityStarter mActivityStarter; 49 private NextAlarmController mNextAlarmController; 50 private SettingsButton mSettingsButton; 51 private View mSettingsContainer; 52 private TextView mAlarmStatus; 53 private View mQsDetailHeader; 54 55 private ImageView mQsDetailHeaderProgress; 56 private QSPanel mQsPanel; 57 private Switch mQsDetailHeaderSwitch; 58 59 private boolean mExpanded; 60 private boolean mAlarmShowing; 61 private boolean mShowingDetail; 62 63 private boolean mDetailTransitioning; 64 private ViewGroup mExpandedGroup; 65 private ViewGroup mDateTimeGroup; 66 private View mEmergencyOnly; 67 private TextView mQsDetailHeaderTitle; 68 private boolean mListening; 69 private AlarmManager.AlarmClockInfo mNextAlarm; 70 71 private QuickQSPanel mHeaderQsPanel; 72 private boolean mShowEmergencyCallsOnly; 73 private float mDateTimeTranslation; 74 private MultiUserSwitch mMultiUserSwitch; 75 private ImageView mMultiUserAvatar; 76 77 public QuickStatusBarHeader(Context context, AttributeSet attrs) { 78 super(context, attrs); 79 } 80 81 @Override 82 protected void onFinishInflate() { 83 super.onFinishInflate(); 84 85 mEmergencyOnly = findViewById(R.id.header_emergency_calls_only); 86 mDateTimeTranslation = mContext.getResources().getDimension( 87 R.dimen.qs_date_anim_translation); 88 mDateTimeGroup = (ViewGroup) findViewById(R.id.date_time_group); 89 mExpandedGroup = (ViewGroup) findViewById(R.id.expanded_group); 90 91 mHeaderQsPanel = (QuickQSPanel) findViewById(R.id.quick_qs_panel); 92 93 mSettingsButton = (SettingsButton) findViewById(R.id.settings_button); 94 mSettingsContainer = findViewById(R.id.settings_button_container); 95 mSettingsButton.setOnClickListener(this); 96 97 mAlarmStatus = (TextView) findViewById(R.id.alarm_status); 98 mAlarmStatus.setOnClickListener(this); 99 100 mQsDetailHeader = findViewById(R.id.qs_detail_header); 101 mQsDetailHeader.setAlpha(0); 102 mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title); 103 mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle); 104 mQsDetailHeaderProgress = (ImageView) findViewById(R.id.qs_detail_header_progress); 105 106 mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch); 107 mMultiUserAvatar = (ImageView) mMultiUserSwitch.findViewById(R.id.multi_user_avatar); 108 109 // RenderThread is doing more harm than good when touching the header (to expand quick 110 // settings), so disable it for this view 111 ((RippleDrawable) getBackground()).setForceSoftware(true); 112 ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true); 113 114 addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 115 @Override 116 public void onLayoutChange(View v, int left, int top, int right, 117 int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { 118 setClipBounds(new Rect(getPaddingLeft(), 0, getWidth() - getPaddingRight(), 119 getHeight())); 120 } 121 }); 122 } 123 124 @Override 125 public int getCollapsedHeight() { 126 return getHeight(); 127 } 128 129 @Override 130 public int getExpandedHeight() { 131 return getHeight(); 132 } 133 134 @Override 135 public void setExpanded(boolean expanded) { 136 mExpanded = expanded; 137 updateEverything(); 138 } 139 140 @Override 141 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) { 142 mNextAlarm = nextAlarm; 143 if (nextAlarm != null) { 144 mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm)); 145 } 146 mAlarmShowing = nextAlarm != null; 147 updateEverything(); 148 } 149 150 @Override 151 public void setExpansion(float headerExpansionFraction) { 152 mExpandedGroup.setAlpha(headerExpansionFraction); 153 mExpandedGroup.setVisibility(headerExpansionFraction > 0 ? View.VISIBLE : View.INVISIBLE); 154 mHeaderQsPanel.setAlpha(1 - headerExpansionFraction); 155 mHeaderQsPanel.setVisibility(headerExpansionFraction < 1 ? View.VISIBLE : View.INVISIBLE); 156 157 mDateTimeGroup.setTranslationY(headerExpansionFraction * mDateTimeTranslation); 158 mEmergencyOnly.setAlpha(headerExpansionFraction); 159 } 160 161 public void setListening(boolean listening) { 162 if (listening == mListening) { 163 return; 164 } 165 mHeaderQsPanel.setListening(listening); 166 mListening = listening; 167 updateListeners(); 168 } 169 170 @Override 171 public void updateEverything() { 172 updateVisibilities(); 173 } 174 175 private void updateVisibilities() { 176 mAlarmStatus.setVisibility(mAlarmShowing ? View.VISIBLE : View.GONE); 177 mQsDetailHeader.setVisibility(mExpanded && mShowingDetail ? View.VISIBLE : View.INVISIBLE); 178 mEmergencyOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly 179 ? View.VISIBLE : View.INVISIBLE); 180 mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility( 181 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE); 182 mMultiUserSwitch.setVisibility(mMultiUserSwitch.hasMultipleUsers() ? View.VISIBLE 183 : View.GONE); 184 } 185 186 private boolean hasMultiUsers() { 187 return false; 188 } 189 190 private void updateListeners() { 191 if (mListening) { 192 mNextAlarmController.addStateChangedCallback(this); 193 } else { 194 mNextAlarmController.removeStateChangedCallback(this); 195 } 196 } 197 198 @Override 199 public void setActivityStarter(ActivityStarter activityStarter) { 200 mActivityStarter = activityStarter; 201 } 202 203 @Override 204 public void setQSPanel(final QSPanel qsPanel) { 205 mQsPanel = qsPanel; 206 setupHost(qsPanel.getHost()); 207 if (mQsPanel != null) { 208 mQsPanel.setCallback(mQsPanelCallback); 209 mMultiUserSwitch.setQsPanel(qsPanel); 210 } 211 } 212 213 public void setupHost(final QSTileHost host) { 214 host.setHeaderView(this); 215 mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this); 216 mHeaderQsPanel.setHost(host); 217 mHeaderQsPanel.setMaxTiles(5); 218 mHeaderQsPanel.setTiles(host.getTiles()); 219 host.addCallback(new QSTile.Host.Callback() { 220 @Override 221 public void onTilesChanged() { 222 mHeaderQsPanel.setTiles(host.getTiles()); 223 } 224 }); 225 } 226 227 @Override 228 public void onClick(View v) { 229 if (v == mSettingsButton) { 230 if (mSettingsButton.isTunerClick()) { 231 if (TunerService.isTunerEnabled(mContext)) { 232 TunerService.showResetRequest(mContext, new Runnable() { 233 @Override 234 public void run() { 235 // Relaunch settings so that the tuner disappears. 236 startSettingsActivity(); 237 } 238 }); 239 } else { 240 Toast.makeText(getContext(), R.string.tuner_toast, Toast.LENGTH_LONG).show(); 241 TunerService.setTunerEnabled(mContext, true); 242 } 243 } 244 startSettingsActivity(); 245 } else if (v == mAlarmStatus && mNextAlarm != null) { 246 PendingIntent showIntent = mNextAlarm.getShowIntent(); 247 if (showIntent != null && showIntent.isActivity()) { 248 mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */); 249 } 250 } 251 } 252 253 private void startSettingsActivity() { 254 mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS), 255 true /* dismissShade */); 256 } 257 258 @Override 259 public void setNextAlarmController(NextAlarmController nextAlarmController) { 260 mNextAlarmController = nextAlarmController; 261 } 262 263 @Override 264 public void setBatteryController(BatteryController batteryController) { 265 // Don't care 266 } 267 268 @Override 269 public void setUserInfoController(UserInfoController userInfoController) { 270 userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() { 271 @Override 272 public void onUserInfoChanged(String name, Drawable picture) { 273 mMultiUserAvatar.setImageDrawable(picture); 274 } 275 }); 276 } 277 278 @Override 279 public void setEmergencyCallsOnly(boolean show) { 280 boolean changed = show != mShowEmergencyCallsOnly; 281 if (changed) { 282 mShowEmergencyCallsOnly = show; 283 if (mExpanded) { 284 updateEverything(); 285 } 286 } 287 } 288 289 private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() { 290 private boolean mScanState; 291 292 @Override 293 public void onShowingDetail(final QSTile.DetailAdapter detail) { 294 mDetailTransitioning = true; 295 post(new Runnable() { 296 @Override 297 public void run() { 298 handleShowingDetail(detail); 299 } 300 }); 301 } 302 303 @Override 304 public void onToggleStateChanged(final boolean state) { 305 post(new Runnable() { 306 @Override 307 public void run() { 308 handleToggleStateChanged(state); 309 } 310 }); 311 312 } 313 314 @Override 315 public void onScanStateChanged(final boolean state) { 316 post(new Runnable() { 317 @Override 318 public void run() { 319 handleScanStateChanged(state); 320 } 321 }); 322 } 323 324 private void handleToggleStateChanged(boolean state) { 325 mQsDetailHeaderSwitch.setChecked(state); 326 } 327 328 private void handleScanStateChanged(boolean state) { 329 if (mScanState == state) return; 330 mScanState = state; 331 final Animatable anim = (Animatable) mQsDetailHeaderProgress.getDrawable(); 332 if (state) { 333 mQsDetailHeaderProgress.animate().alpha(1f); 334 anim.start(); 335 } else { 336 mQsDetailHeaderProgress.animate().alpha(0f); 337 anim.stop(); 338 } 339 } 340 341 private void handleShowingDetail(final QSTile.DetailAdapter detail) { 342 final boolean showingDetail = detail != null; 343 transition(mDateTimeGroup, !showingDetail); 344 transition(mExpandedGroup, !showingDetail); 345 if (mAlarmShowing) { 346 transition(mAlarmStatus, !showingDetail); 347 } 348 transition(mQsDetailHeader, showingDetail); 349 mShowingDetail = showingDetail; 350 if (showingDetail) { 351 mQsDetailHeaderTitle.setText(detail.getTitle()); 352 final Boolean toggleState = detail.getToggleState(); 353 if (toggleState == null) { 354 mQsDetailHeaderSwitch.setVisibility(INVISIBLE); 355 mQsDetailHeader.setClickable(false); 356 } else { 357 mQsDetailHeaderSwitch.setVisibility(VISIBLE); 358 mQsDetailHeaderSwitch.setChecked(toggleState); 359 mQsDetailHeader.setClickable(true); 360 mQsDetailHeader.setOnClickListener(new OnClickListener() { 361 @Override 362 public void onClick(View v) { 363 boolean checked = !mQsDetailHeaderSwitch.isChecked(); 364 mQsDetailHeaderSwitch.setChecked(checked); 365 detail.setToggleState(checked); 366 } 367 }); 368 } 369 } else { 370 mQsDetailHeader.setClickable(false); 371 } 372 } 373 374 private void transition(final View v, final boolean in) { 375 if (in) { 376 v.bringToFront(); 377 v.setVisibility(VISIBLE); 378 } 379 if (v.hasOverlappingRendering()) { 380 v.animate().withLayer(); 381 } 382 v.animate() 383 .alpha(in ? 1 : 0) 384 .withEndAction(new Runnable() { 385 @Override 386 public void run() { 387 if (!in) { 388 v.setVisibility(INVISIBLE); 389 } 390 mDetailTransitioning = false; 391 } 392 }) 393 .start(); 394 } 395 }; 396} 397