blob: 3199decb3ec165fd045e1f6226faec484e6b3d35 [file] [log] [blame]
/*
* Copyright (C) 2017 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.qs;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_DATE;
import android.app.ActivityManager;
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.PorterDuff.Mode;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.os.UserManager;
import android.provider.AlarmClock;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.keyguard.KeyguardStatusView;
import com.android.settingslib.Utils;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.systemui.Dependency;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.R.dimen;
import com.android.systemui.R.id;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.qs.TouchAnimator.ListenerAdapter;
import com.android.systemui.statusbar.phone.ExpandableIndicator;
import com.android.systemui.statusbar.phone.MultiUserSwitch;
import com.android.systemui.statusbar.phone.SettingsButton;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
import com.android.systemui.tuner.TunerService;
public class QSFooterImpl extends FrameLayout implements QSFooter,
NextAlarmChangeCallback, OnClickListener, OnUserInfoChangedListener, EmergencyListener,
SignalCallback {
private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
private ActivityStarter mActivityStarter;
private NextAlarmController mNextAlarmController;
private UserInfoController mUserInfoController;
private SettingsButton mSettingsButton;
protected View mSettingsContainer;
private TextView mAlarmStatus;
private View mAlarmStatusCollapsed;
private View mDate;
private QSPanel mQsPanel;
private boolean mExpanded;
private boolean mAlarmShowing;
protected ExpandableIndicator mExpandIndicator;
private boolean mListening;
private AlarmManager.AlarmClockInfo mNextAlarm;
private boolean mShowEmergencyCallsOnly;
protected MultiUserSwitch mMultiUserSwitch;
private ImageView mMultiUserAvatar;
protected TouchAnimator mSettingsAlpha;
private float mExpansionAmount;
protected View mEdit;
private TouchAnimator mAnimator;
private View mDateTimeGroup;
private boolean mKeyguardShowing;
private TouchAnimator mAlarmAnimator;
public QSFooterImpl(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
Resources res = getResources();
mEdit = findViewById(android.R.id.edit);
mEdit.setOnClickListener(view ->
Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
mQsPanel.showEdit(view)));
mDateTimeGroup = findViewById(id.date_time_alarm_group);
mDate = findViewById(R.id.date);
mExpandIndicator = findViewById(R.id.expand_indicator);
mSettingsButton = findViewById(R.id.settings_button);
mSettingsContainer = findViewById(R.id.settings_button_container);
mSettingsButton.setOnClickListener(this);
mAlarmStatusCollapsed = findViewById(R.id.alarm_status_collapsed);
mAlarmStatus = findViewById(R.id.alarm_status);
mDateTimeGroup.setOnClickListener(this);
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
updateResources();
mNextAlarmController = Dependency.get(NextAlarmController.class);
mUserInfoController = Dependency.get(UserInfoController.class);
mActivityStarter = Dependency.get(ActivityStarter.class);
addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
oldBottom) -> updateAnimator(right - left));
}
private void updateAnimator(int width) {
int numTiles = QuickQSPanel.getNumQuickTiles(mContext);
int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
- mContext.getResources().getDimensionPixelSize(dimen.qs_quick_tile_padding);
int remaining = (width - numTiles * size) / (numTiles - 1);
int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
mAnimator = new Builder()
.addFloat(mSettingsContainer, "translationX",
isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
.addFloat(mSettingsButton, "rotation", -120, 0)
.build();
if (mAlarmShowing) {
int translate = isLayoutRtl() ? mDate.getWidth() : -mDate.getWidth();
mAlarmAnimator = new Builder().addFloat(mDate, "alpha", 1, 0)
.addFloat(mDateTimeGroup, "translationX", 0, translate)
.addFloat(mAlarmStatus, "alpha", 0, 1)
.setListener(new ListenerAdapter() {
@Override
public void onAnimationAtStart() {
mAlarmStatus.setVisibility(View.GONE);
}
@Override
public void onAnimationStarted() {
mAlarmStatus.setVisibility(View.VISIBLE);
}
}).build();
} else {
mAlarmAnimator = null;
mAlarmStatus.setVisibility(View.GONE);
mDate.setAlpha(1);
mDateTimeGroup.setTranslationX(0);
}
setExpansion(mExpansionAmount);
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateResources();
}
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
updateResources();
}
private void updateResources() {
FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size);
updateSettingsAnimator();
}
private void updateSettingsAnimator() {
mSettingsAlpha = createSettingsAlphaAnimator();
final boolean isRtl = isLayoutRtl();
if (isRtl && mDate.getWidth() == 0) {
mDate.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
mDate.setPivotX(getWidth());
mDate.removeOnLayoutChangeListener(this);
}
});
} else {
mDate.setPivotX(isRtl ? mDate.getWidth() : 0);
}
}
@Nullable
private TouchAnimator createSettingsAlphaAnimator() {
return new TouchAnimator.Builder()
.addFloat(mEdit, "alpha", 0, 1)
.addFloat(mMultiUserSwitch, "alpha", 0, 1)
.build();
}
@Override
public void setKeyguardShowing(boolean keyguardShowing) {
mKeyguardShowing = keyguardShowing;
setExpansion(mExpansionAmount);
}
@Override
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
updateEverything();
}
@Override
public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
mNextAlarm = nextAlarm;
if (nextAlarm != null) {
String alarmString = KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm);
mAlarmStatus.setText(alarmString);
mAlarmStatus.setContentDescription(mContext.getString(
R.string.accessibility_quick_settings_alarm, alarmString));
mAlarmStatusCollapsed.setContentDescription(mContext.getString(
R.string.accessibility_quick_settings_alarm, alarmString));
}
if (mAlarmShowing != (nextAlarm != null)) {
mAlarmShowing = nextAlarm != null;
updateAnimator(getWidth());
updateEverything();
}
}
@Override
public void setExpansion(float headerExpansionFraction) {
mExpansionAmount = headerExpansionFraction;
if (mAnimator != null) mAnimator.setPosition(headerExpansionFraction);
if (mAlarmAnimator != null) mAlarmAnimator.setPosition(
mKeyguardShowing ? 0 : headerExpansionFraction);
if (mSettingsAlpha != null) {
mSettingsAlpha.setPosition(headerExpansionFraction);
}
updateAlarmVisibilities();
mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
}
@Override
@VisibleForTesting
public void onDetachedFromWindow() {
setListening(false);
super.onDetachedFromWindow();
}
private void updateAlarmVisibilities() {
mAlarmStatusCollapsed.setVisibility(mAlarmShowing ? View.VISIBLE : View.GONE);
}
@Override
public void setListening(boolean listening) {
if (listening == mListening) {
return;
}
mListening = listening;
updateListeners();
}
@Override
public View getExpandView() {
return findViewById(R.id.expand_indicator);
}
public void updateEverything() {
post(() -> {
updateVisibilities();
setClickable(false);
});
}
private void updateVisibilities() {
updateAlarmVisibilities();
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
mMultiUserSwitch.setVisibility(mExpanded && mMultiUserSwitch.hasMultipleUsers() && !isDemo
? View.VISIBLE : View.INVISIBLE);
mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
}
private void updateListeners() {
if (mListening) {
mNextAlarmController.addCallback(this);
mUserInfoController.addCallback(this);
if (Dependency.get(NetworkController.class).hasVoiceCallingFeature()) {
Dependency.get(NetworkController.class).addEmergencyListener(this);
Dependency.get(NetworkController.class).addCallback(this);
}
} else {
mNextAlarmController.removeCallback(this);
mUserInfoController.removeCallback(this);
Dependency.get(NetworkController.class).removeEmergencyListener(this);
Dependency.get(NetworkController.class).removeCallback(this);
}
}
@Override
public void setQSPanel(final QSPanel qsPanel) {
mQsPanel = qsPanel;
if (mQsPanel != null) {
mMultiUserSwitch.setQsPanel(qsPanel);
}
}
@Override
public void onClick(View v) {
if (v == mSettingsButton) {
if (!Dependency.get(DeviceProvisionedController.class).isCurrentUserSetup()) {
// If user isn't setup just unlock the device and dump them back at SUW.
mActivityStarter.postQSRunnableDismissingKeyguard(() -> { });
return;
}
MetricsLogger.action(mContext,
mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
: MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
if (mSettingsButton.isTunerClick()) {
Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
if (TunerService.isTunerEnabled(mContext)) {
TunerService.showResetRequest(mContext, () -> {
// 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 {
startSettingsActivity();
}
} else if (v == mDateTimeGroup) {
Dependency.get(MetricsLogger.class).action(ACTION_QS_DATE,
mNextAlarm != null);
if (mNextAlarm != null) {
PendingIntent showIntent = mNextAlarm.getShowIntent();
mActivityStarter.startPendingIntentDismissingKeyguard(showIntent);
} else {
mActivityStarter.postStartActivityDismissingKeyguard(new Intent(
AlarmClock.ACTION_SHOW_ALARMS), 0);
}
}
}
private void startSettingsActivity() {
mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
true /* dismissShade */);
}
@Override
public void setEmergencyCallsOnly(boolean show) {
boolean changed = show != mShowEmergencyCallsOnly;
if (changed) {
mShowEmergencyCallsOnly = show;
if (mExpanded) {
updateEverything();
}
}
}
@Override
public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
if (picture != null &&
UserManager.get(mContext).isGuestUser(ActivityManager.getCurrentUser()) &&
!(picture instanceof UserIconDrawable)) {
picture = picture.getConstantState().newDrawable(mContext.getResources()).mutate();
picture.setColorFilter(
Utils.getColorAttr(mContext, android.R.attr.colorForeground),
Mode.SRC_IN);
}
mMultiUserAvatar.setImageDrawable(picture);
}
}