/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.statusbar.phone;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.NetworkControllerImpl.EmergencyListener;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tuner.TunerService;
import java.text.NumberFormat;
/**
* The view to manage the header area in the expanded status bar.
*/
public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener,
BatteryController.BatteryStateChangeCallback, NextAlarmController.NextAlarmChangeCallback,
EmergencyListener {
private boolean mExpanded;
private boolean mListening;
private ViewGroup mSystemIconsContainer;
private View mSystemIconsSuperContainer;
private View mDateGroup;
private View mClock;
private TextView mTime;
private TextView mAmPm;
private MultiUserSwitch mMultiUserSwitch;
private ImageView mMultiUserAvatar;
private TextView mDateCollapsed;
private TextView mDateExpanded;
private LinearLayout mSystemIcons;
private View mSignalCluster;
private SettingsButton mSettingsButton;
private View mSettingsContainer;
private View mQsDetailHeader;
private TextView mQsDetailHeaderTitle;
private Switch mQsDetailHeaderSwitch;
private ImageView mQsDetailHeaderProgress;
private TextView mEmergencyCallsOnly;
private TextView mBatteryLevel;
private TextView mAlarmStatus;
private boolean mShowEmergencyCallsOnly;
private boolean mAlarmShowing;
private AlarmManager.AlarmClockInfo mNextAlarm;
private int mCollapsedHeight;
private int mExpandedHeight;
private int mMultiUserExpandedMargin;
private int mMultiUserCollapsedMargin;
private int mClockMarginBottomExpanded;
private int mClockMarginBottomCollapsed;
private int mMultiUserSwitchWidthCollapsed;
private int mMultiUserSwitchWidthExpanded;
private int mClockCollapsedSize;
private int mClockExpandedSize;
/**
* In collapsed QS, the clock and avatar are scaled down a bit post-layout to allow for a nice
* transition. These values determine that factor.
*/
private float mClockCollapsedScaleFactor;
private float mAvatarCollapsedScaleFactor;
private ActivityStarter mActivityStarter;
private BatteryController mBatteryController;
private NextAlarmController mNextAlarmController;
private QSPanel mQSPanel;
private final Rect mClipBounds = new Rect();
private boolean mCaptureValues;
private boolean mSignalClusterDetached;
private final LayoutValues mCollapsedValues = new LayoutValues();
private final LayoutValues mExpandedValues = new LayoutValues();
private final LayoutValues mCurrentValues = new LayoutValues();
private float mCurrentT;
private boolean mShowingDetail;
private boolean mDetailTransitioning;
public StatusBarHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mSystemIconsSuperContainer = findViewById(R.id.system_icons_super_container);
mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
mSystemIconsSuperContainer.setOnClickListener(this);
mDateGroup = findViewById(R.id.date_group);
mClock = findViewById(R.id.clock);
mTime = (TextView) findViewById(R.id.time_view);
mAmPm = (TextView) findViewById(R.id.am_pm_view);
mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar);
mDateCollapsed = (TextView) findViewById(R.id.date_collapsed);
mDateExpanded = (TextView) findViewById(R.id.date_expanded);
mSettingsButton = (SettingsButton) findViewById(R.id.settings_button);
mSettingsContainer = findViewById(R.id.settings_button_container);
mSettingsButton.setOnClickListener(this);
mQsDetailHeader = findViewById(R.id.qs_detail_header);
mQsDetailHeader.setAlpha(0);
mQsDetailHeaderTitle = (TextView) mQsDetailHeader.findViewById(android.R.id.title);
mQsDetailHeaderSwitch = (Switch) mQsDetailHeader.findViewById(android.R.id.toggle);
mQsDetailHeaderProgress = (ImageView) findViewById(R.id.qs_detail_header_progress);
mEmergencyCallsOnly = (TextView) findViewById(R.id.header_emergency_calls_only);
mBatteryLevel = (TextView) findViewById(R.id.battery_level);
mAlarmStatus = (TextView) findViewById(R.id.alarm_status);
mAlarmStatus.setOnClickListener(this);
mSignalCluster = findViewById(R.id.signal_cluster);
mSystemIcons = (LinearLayout) findViewById(R.id.system_icons);
loadDimens();
updateVisibilities();
updateClockScale();
updateAvatarScale();
addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right,
int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
if ((right - left) != (oldRight - oldLeft)) {
// width changed, update clipping
setClipping(getHeight());
}
boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
mTime.setPivotX(rtl ? mTime.getWidth() : 0);
mTime.setPivotY(mTime.getBaseline());
updateAmPmTranslation();
}
});
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRect(mClipBounds);
}
});
requestCaptureValues();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mCaptureValues) {
if (mExpanded) {
captureLayoutValues(mExpandedValues);
} else {
captureLayoutValues(mCollapsedValues);
}
mCaptureValues = false;
updateLayoutValues(mCurrentT);
}
mAlarmStatus.setX(mDateGroup.getLeft() + mDateCollapsed.getRight());
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
FontSizeUtils.updateFontSize(mBatteryLevel, R.dimen.battery_level_text_size);
FontSizeUtils.updateFontSize(mEmergencyCallsOnly,
R.dimen.qs_emergency_calls_only_text_size);
FontSizeUtils.updateFontSize(mDateCollapsed, R.dimen.qs_date_collapsed_size);
FontSizeUtils.updateFontSize(mDateExpanded, R.dimen.qs_date_collapsed_size);
FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size);
FontSizeUtils.updateFontSize(this, android.R.id.title, R.dimen.qs_detail_header_text_size);
FontSizeUtils.updateFontSize(this, android.R.id.toggle, R.dimen.qs_detail_header_text_size);
FontSizeUtils.updateFontSize(mAmPm, R.dimen.qs_time_collapsed_size);
FontSizeUtils.updateFontSize(this, R.id.empty_time_view, R.dimen.qs_time_expanded_size);
mEmergencyCallsOnly.setText(com.android.internal.R.string.emergency_calls_only);
mClockCollapsedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size);
mClockExpandedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
mClockCollapsedScaleFactor = (float) mClockCollapsedSize / (float) mClockExpandedSize;
updateClockScale();
updateClockCollapsedMargin();
}
private void updateClockCollapsedMargin() {
Resources res = getResources();
int padding = res.getDimensionPixelSize(R.dimen.clock_collapsed_bottom_margin);
int largePadding = res.getDimensionPixelSize(
R.dimen.clock_collapsed_bottom_margin_large_text);
float largeFactor = (MathUtils.constrain(getResources().getConfiguration().fontScale, 1.0f,
FontSizeUtils.LARGE_TEXT_SCALE) - 1f) / (FontSizeUtils.LARGE_TEXT_SCALE - 1f);
mClockMarginBottomCollapsed = Math.round((1 - largeFactor) * padding + largeFactor * largePadding);
requestLayout();
}
private void requestCaptureValues() {
mCaptureValues = true;
requestLayout();
}
private void loadDimens() {
mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height);
mExpandedHeight = getResources().getDimensionPixelSize(
R.dimen.status_bar_header_height_expanded);
mMultiUserExpandedMargin =
getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin);
mMultiUserCollapsedMargin =
getResources().getDimensionPixelSize(R.dimen.multi_user_switch_collapsed_margin);
mClockMarginBottomExpanded =
getResources().getDimensionPixelSize(R.dimen.clock_expanded_bottom_margin);
updateClockCollapsedMargin();
mMultiUserSwitchWidthCollapsed =
getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_collapsed);
mMultiUserSwitchWidthExpanded =
getResources().getDimensionPixelSize(R.dimen.multi_user_switch_width_expanded);
mAvatarCollapsedScaleFactor =
getResources().getDimensionPixelSize(R.dimen.multi_user_avatar_collapsed_size)
/ (float) mMultiUserAvatar.getLayoutParams().width;
mClockCollapsedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size);
mClockExpandedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
mClockCollapsedScaleFactor = (float) mClockCollapsedSize / (float) mClockExpandedSize;
}
public void setActivityStarter(ActivityStarter activityStarter) {
mActivityStarter = activityStarter;
}
public void setBatteryController(BatteryController batteryController) {
mBatteryController = batteryController;
((BatteryMeterView) findViewById(R.id.battery)).setBatteryController(batteryController);
}
public void setNextAlarmController(NextAlarmController nextAlarmController) {
mNextAlarmController = nextAlarmController;
}
public int getCollapsedHeight() {
return mCollapsedHeight;
}
public int getExpandedHeight() {
return mExpandedHeight;
}
public void setListening(boolean listening) {
if (listening == mListening) {
return;
}
mListening = listening;
updateListeners();
}
public void setExpanded(boolean expanded) {
boolean changed = expanded != mExpanded;
mExpanded = expanded;
if (changed) {
updateEverything();
}
}
public void updateEverything() {
updateHeights();
updateVisibilities();
updateSystemIconsLayoutParams();
updateClickTargets();
updateMultiUserSwitch();
updateClockScale();
updateAvatarScale();
updateClockLp();
requestCaptureValues();
}
private void updateHeights() {
int height = mExpanded ? mExpandedHeight : mCollapsedHeight;
ViewGroup.LayoutParams lp = getLayoutParams();
if (lp.height != height) {
lp.height = height;
setLayoutParams(lp);
}
}
private void updateVisibilities() {
mDateCollapsed.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
mDateExpanded.setVisibility(mExpanded && mAlarmShowing ? View.INVISIBLE : View.VISIBLE);
mAlarmStatus.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
mSettingsContainer.setVisibility(mExpanded ? View.VISIBLE : View.INVISIBLE);
mQsDetailHeader.setVisibility(mExpanded && mShowingDetail? View.VISIBLE : View.INVISIBLE);
if (mSignalCluster != null) {
updateSignalClusterDetachment();
}
mEmergencyCallsOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly ? VISIBLE : GONE);
mBatteryLevel.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
}
private void updateSignalClusterDetachment() {
boolean detached = mExpanded;
if (detached != mSignalClusterDetached) {
if (detached) {
getOverlay().add(mSignalCluster);
} else {
reattachSignalCluster();
}
}
mSignalClusterDetached = detached;
}
private void reattachSignalCluster() {
getOverlay().remove(mSignalCluster);
mSystemIcons.addView(mSignalCluster, 1);
}
private void updateSystemIconsLayoutParams() {
RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsSuperContainer.getLayoutParams();
int rule = mExpanded
? mSettingsContainer.getId()
: mMultiUserSwitch.getId();
if (rule != lp.getRules()[RelativeLayout.START_OF]) {
lp.addRule(RelativeLayout.START_OF, rule);
mSystemIconsSuperContainer.setLayoutParams(lp);
}
}
private void updateListeners() {
if (mListening) {
mBatteryController.addStateChangedCallback(this);
mNextAlarmController.addStateChangedCallback(this);
} else {
mBatteryController.removeStateChangedCallback(this);
mNextAlarmController.removeStateChangedCallback(this);
}
}
private void updateAvatarScale() {
if (mExpanded) {
mMultiUserAvatar.setScaleX(1f);
mMultiUserAvatar.setScaleY(1f);
} else {
mMultiUserAvatar.setScaleX(mAvatarCollapsedScaleFactor);
mMultiUserAvatar.setScaleY(mAvatarCollapsedScaleFactor);
}
}
private void updateClockScale() {
mTime.setTextSize(TypedValue.COMPLEX_UNIT_PX, mExpanded
? mClockExpandedSize
: mClockCollapsedSize);
mTime.setScaleX(1f);
mTime.setScaleY(1f);
updateAmPmTranslation();
}
private void updateAmPmTranslation() {
boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
mAmPm.setTranslationX((rtl ? 1 : -1) * mTime.getWidth() * (1 - mTime.getScaleX()));
}
@Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
String percentage = NumberFormat.getPercentInstance().format((double) level / 100.0);
mBatteryLevel.setText(percentage);
}
@Override
public void onPowerSaveChanged() {
// could not care less
}
@Override
public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
mNextAlarm = nextAlarm;
if (nextAlarm != null) {
mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
}
mAlarmShowing = nextAlarm != null;
updateEverything();
requestCaptureValues();
}
private void updateClickTargets() {
mMultiUserSwitch.setClickable(mExpanded);
mMultiUserSwitch.setFocusable(mExpanded);
mSystemIconsSuperContainer.setClickable(mExpanded);
mSystemIconsSuperContainer.setFocusable(mExpanded);
mAlarmStatus.setClickable(mNextAlarm != null && mNextAlarm.getShowIntent() != null);
}
private void updateClockLp() {
int marginBottom = mExpanded
? mClockMarginBottomExpanded
: mClockMarginBottomCollapsed;
LayoutParams lp = (LayoutParams) mDateGroup.getLayoutParams();
if (marginBottom != lp.bottomMargin) {
lp.bottomMargin = marginBottom;
mDateGroup.setLayoutParams(lp);
}
}
private void updateMultiUserSwitch() {
int marginEnd;
int width;
if (mExpanded) {
marginEnd = mMultiUserExpandedMargin;
width = mMultiUserSwitchWidthExpanded;
} else {
marginEnd = mMultiUserCollapsedMargin;
width = mMultiUserSwitchWidthCollapsed;
}
MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
if (marginEnd != lp.getMarginEnd() || lp.width != width) {
lp.setMarginEnd(marginEnd);
lp.width = width;
mMultiUserSwitch.setLayoutParams(lp);
}
}
public void setExpansion(float t) {
if (!mExpanded) {
t = 0f;
}
mCurrentT = t;
float height = mCollapsedHeight + t * (mExpandedHeight - mCollapsedHeight);
if (height < mCollapsedHeight) {
height = mCollapsedHeight;
}
if (height > mExpandedHeight) {
height = mExpandedHeight;
}
setClipping(height);
updateLayoutValues(t);
}
private void updateLayoutValues(float t) {
if (mCaptureValues) {
return;
}
mCurrentValues.interpoloate(mCollapsedValues, mExpandedValues, t);
applyLayoutValues(mCurrentValues);
}
private void setClipping(float height) {
mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height);
setClipBounds(mClipBounds);
invalidateOutline();
}
public void setUserInfoController(UserInfoController userInfoController) {
userInfoController.addListener(new UserInfoController.OnUserInfoChangedListener() {
@Override
public void onUserInfoChanged(String name, Drawable picture) {
mMultiUserAvatar.setImageDrawable(picture);
}
});
}
@Override
public void onClick(View v) {
if (v == mSettingsButton) {
if (mSettingsButton.isTunerClick()) {
if (TunerService.isTunerEnabled(mContext)) {
TunerService.showResetRequest(mContext, new Runnable() {
@Override
public void run() {
// Relaunch settings so that the tuner disappears.
startSettingsActivity();
}
});
} else {
Toast.makeText(getContext(), R.string.tuner_toast, Toast.LENGTH_LONG).show();
TunerService.setTunerEnabled(mContext, true);
}
}
startSettingsActivity();
} else if (v == mSystemIconsSuperContainer) {
startBatteryActivity();
} else if (v == mAlarmStatus && mNextAlarm != null) {
PendingIntent showIntent = mNextAlarm.getShowIntent();
if (showIntent != null && showIntent.isActivity()) {
mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */);
}
}
}
private void startSettingsActivity() {
mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
true /* dismissShade */);
}
private void startBatteryActivity() {
mActivityStarter.startActivity(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY),
true /* dismissShade */);
}
public void setQSPanel(QSPanel qsp) {
mQSPanel = qsp;
if (mQSPanel != null) {
mQSPanel.setCallback(mQsPanelCallback);
}
mMultiUserSwitch.setQsPanel(qsp);
}
@Override
public boolean shouldDelayChildPressedState() {
return true;
}
@Override
public void setEmergencyCallsOnly(boolean show) {
boolean changed = show != mShowEmergencyCallsOnly;
if (changed) {
mShowEmergencyCallsOnly = show;
if (mExpanded) {
updateEverything();
requestCaptureValues();
}
}
}
@Override
protected void dispatchSetPressed(boolean pressed) {
// We don't want that everything lights up when we click on the header, so block the request
// here.
}
private void captureLayoutValues(LayoutValues target) {
target.timeScale = mExpanded ? 1f : mClockCollapsedScaleFactor;
target.clockY = mClock.getBottom();
target.dateY = mDateGroup.getTop();
target.emergencyCallsOnlyAlpha = getAlphaForVisibility(mEmergencyCallsOnly);
target.alarmStatusAlpha = getAlphaForVisibility(mAlarmStatus);
target.dateCollapsedAlpha = getAlphaForVisibility(mDateCollapsed);
target.dateExpandedAlpha = getAlphaForVisibility(mDateExpanded);
target.avatarScale = mMultiUserAvatar.getScaleX();
target.avatarX = mMultiUserSwitch.getLeft() + mMultiUserAvatar.getLeft();
target.avatarY = mMultiUserSwitch.getTop() + mMultiUserAvatar.getTop();
if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
target.batteryX = mSystemIconsSuperContainer.getLeft()
+ mSystemIconsContainer.getRight();
} else {
target.batteryX = mSystemIconsSuperContainer.getLeft()
+ mSystemIconsContainer.getLeft();
}
target.batteryY = mSystemIconsSuperContainer.getTop() + mSystemIconsContainer.getTop();
target.batteryLevelAlpha = getAlphaForVisibility(mBatteryLevel);
target.settingsAlpha = getAlphaForVisibility(mSettingsContainer);
target.settingsTranslation = mExpanded
? 0
: mMultiUserSwitch.getLeft() - mSettingsContainer.getLeft();
target.signalClusterAlpha = mSignalClusterDetached ? 0f : 1f;
target.settingsRotation = !mExpanded ? 90f : 0f;
}
private float getAlphaForVisibility(View v) {
return v == null || v.getVisibility() == View.VISIBLE ? 1f : 0f;
}
private void applyAlpha(View v, float alpha) {
if (v == null || v.getVisibility() == View.GONE) {
return;
}
if (alpha == 0f) {
v.setVisibility(View.INVISIBLE);
} else {
v.setVisibility(View.VISIBLE);
v.setAlpha(alpha);
}
}
private void applyLayoutValues(LayoutValues values) {
mTime.setScaleX(values.timeScale);
mTime.setScaleY(values.timeScale);
mClock.setY(values.clockY - mClock.getHeight());
mDateGroup.setY(values.dateY);
mAlarmStatus.setY(values.dateY - mAlarmStatus.getPaddingTop());
mMultiUserAvatar.setScaleX(values.avatarScale);
mMultiUserAvatar.setScaleY(values.avatarScale);
mMultiUserAvatar.setX(values.avatarX - mMultiUserSwitch.getLeft());
mMultiUserAvatar.setY(values.avatarY - mMultiUserSwitch.getTop());
if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
mSystemIconsSuperContainer.setX(values.batteryX - mSystemIconsContainer.getRight());
} else {
mSystemIconsSuperContainer.setX(values.batteryX - mSystemIconsContainer.getLeft());
}
mSystemIconsSuperContainer.setY(values.batteryY - mSystemIconsContainer.getTop());
if (mSignalCluster != null && mExpanded) {
if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
mSignalCluster.setX(mSystemIconsSuperContainer.getX()
- mSignalCluster.getWidth());
} else {
mSignalCluster.setX(mSystemIconsSuperContainer.getX()
+ mSystemIconsSuperContainer.getWidth());
}
mSignalCluster.setY(
mSystemIconsSuperContainer.getY() + mSystemIconsSuperContainer.getHeight()/2
- mSignalCluster.getHeight()/2);
} else if (mSignalCluster != null) {
mSignalCluster.setTranslationX(0f);
mSignalCluster.setTranslationY(0f);
}
if (!mSettingsButton.isAnimating()) {
mSettingsContainer.setTranslationY(mSystemIconsSuperContainer.getTranslationY());
mSettingsContainer.setTranslationX(values.settingsTranslation);
mSettingsButton.setRotation(values.settingsRotation);
}
applyAlpha(mEmergencyCallsOnly, values.emergencyCallsOnlyAlpha);
if (!mShowingDetail && !mDetailTransitioning) {
// Otherwise it needs to stay invisible
applyAlpha(mAlarmStatus, values.alarmStatusAlpha);
}
applyAlpha(mDateCollapsed, values.dateCollapsedAlpha);
applyAlpha(mDateExpanded, values.dateExpandedAlpha);
applyAlpha(mBatteryLevel, values.batteryLevelAlpha);
applyAlpha(mSettingsContainer, values.settingsAlpha);
applyAlpha(mSignalCluster, values.signalClusterAlpha);
if (!mExpanded) {
mTime.setScaleX(1f);
mTime.setScaleY(1f);
}
updateAmPmTranslation();
}
/**
* Captures all layout values (position, visibility) for a certain state. This is used for
* animations.
*/
private static final class LayoutValues {
float dateExpandedAlpha;
float dateCollapsedAlpha;
float emergencyCallsOnlyAlpha;
float alarmStatusAlpha;
float timeScale = 1f;
float clockY;
float dateY;
float avatarScale;
float avatarX;
float avatarY;
float batteryX;
float batteryY;
float batteryLevelAlpha;
float settingsAlpha;
float settingsTranslation;
float signalClusterAlpha;
float settingsRotation;
public void interpoloate(LayoutValues v1, LayoutValues v2, float t) {
timeScale = v1.timeScale * (1 - t) + v2.timeScale * t;
clockY = v1.clockY * (1 - t) + v2.clockY * t;
dateY = v1.dateY * (1 - t) + v2.dateY * t;
avatarScale = v1.avatarScale * (1 - t) + v2.avatarScale * t;
avatarX = v1.avatarX * (1 - t) + v2.avatarX * t;
avatarY = v1.avatarY * (1 - t) + v2.avatarY * t;
batteryX = v1.batteryX * (1 - t) + v2.batteryX * t;
batteryY = v1.batteryY * (1 - t) + v2.batteryY * t;
settingsTranslation = v1.settingsTranslation * (1 - t) + v2.settingsTranslation * t;
float t1 = Math.max(0, t - 0.5f) * 2;
settingsRotation = v1.settingsRotation * (1 - t1) + v2.settingsRotation * t1;
emergencyCallsOnlyAlpha =
v1.emergencyCallsOnlyAlpha * (1 - t1) + v2.emergencyCallsOnlyAlpha * t1;
float t2 = Math.min(1, 2 * t);
signalClusterAlpha = v1.signalClusterAlpha * (1 - t2) + v2.signalClusterAlpha * t2;
float t3 = Math.max(0, t - 0.7f) / 0.3f;
batteryLevelAlpha = v1.batteryLevelAlpha * (1 - t3) + v2.batteryLevelAlpha * t3;
settingsAlpha = v1.settingsAlpha * (1 - t3) + v2.settingsAlpha * t3;
dateExpandedAlpha = v1.dateExpandedAlpha * (1 - t3) + v2.dateExpandedAlpha * t3;
dateCollapsedAlpha = v1.dateCollapsedAlpha * (1 - t3) + v2.dateCollapsedAlpha * t3;
alarmStatusAlpha = v1.alarmStatusAlpha * (1 - t3) + v2.alarmStatusAlpha * t3;
}
}
private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
private boolean mScanState;
@Override
public void onToggleStateChanged(final boolean state) {
post(new Runnable() {
@Override
public void run() {
handleToggleStateChanged(state);
}
});
}
@Override
public void onShowingDetail(final QSTile.DetailAdapter detail) {
mDetailTransitioning = true;
post(new Runnable() {
@Override
public void run() {
handleShowingDetail(detail);
}
});
}
@Override
public void onScanStateChanged(final boolean state) {
post(new Runnable() {
@Override
public void run() {
handleScanStateChanged(state);
}
});
}
private void handleToggleStateChanged(boolean state) {
mQsDetailHeaderSwitch.setChecked(state);
}
private void handleScanStateChanged(boolean state) {
if (mScanState == state) return;
mScanState = state;
final Animatable anim = (Animatable) mQsDetailHeaderProgress.getDrawable();
if (state) {
mQsDetailHeaderProgress.animate().alpha(1f);
anim.start();
} else {
mQsDetailHeaderProgress.animate().alpha(0f);
anim.stop();
}
}
private void handleShowingDetail(final QSTile.DetailAdapter detail) {
final boolean showingDetail = detail != null;
transition(mClock, !showingDetail);
transition(mDateGroup, !showingDetail);
if (mAlarmShowing) {
transition(mAlarmStatus, !showingDetail);
}
transition(mQsDetailHeader, showingDetail);
mShowingDetail = showingDetail;
if (showingDetail) {
mQsDetailHeaderTitle.setText(detail.getTitle());
final Boolean toggleState = detail.getToggleState();
if (toggleState == null) {
mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
mQsDetailHeader.setClickable(false);
} else {
mQsDetailHeaderSwitch.setVisibility(VISIBLE);
mQsDetailHeaderSwitch.setChecked(toggleState);
mQsDetailHeader.setClickable(true);
mQsDetailHeader.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
boolean checked = !mQsDetailHeaderSwitch.isChecked();
mQsDetailHeaderSwitch.setChecked(checked);
detail.setToggleState(checked);
}
});
}
} else {
mQsDetailHeader.setClickable(false);
}
}
private void transition(final View v, final boolean in) {
if (in) {
v.bringToFront();
v.setVisibility(VISIBLE);
}
if (v.hasOverlappingRendering()) {
v.animate().withLayer();
}
v.animate()
.alpha(in ? 1 : 0)
.withEndAction(new Runnable() {
@Override
public void run() {
if (!in) {
v.setVisibility(INVISIBLE);
}
mDetailTransitioning = false;
}
})
.start();
}
};
}