/*
|
* 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 android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
|
|
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
|
|
import android.content.Context;
|
import android.content.Intent;
|
import android.content.res.Configuration;
|
import android.database.ContentObserver;
|
import android.graphics.PorterDuff.Mode;
|
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.RippleDrawable;
|
import android.net.Uri;
|
import android.os.Build;
|
import android.os.Bundle;
|
import android.os.Handler;
|
import android.os.UserHandle;
|
import android.os.UserManager;
|
import android.provider.Settings;
|
import android.util.AttributeSet;
|
import android.view.View;
|
import android.view.View.OnClickListener;
|
import android.view.accessibility.AccessibilityNodeInfo;
|
import android.widget.FrameLayout;
|
import android.widget.ImageView;
|
import android.widget.TextView;
|
import android.widget.Toast;
|
|
import androidx.annotation.Nullable;
|
import androidx.annotation.VisibleForTesting;
|
|
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.nano.MetricsProto;
|
import com.android.keyguard.KeyguardUpdateMonitor;
|
import com.android.settingslib.Utils;
|
import com.android.settingslib.development.DevelopmentSettingsEnabler;
|
import com.android.settingslib.drawable.UserIconDrawable;
|
import com.android.systemui.Dependency;
|
import com.android.systemui.R;
|
import com.android.systemui.R.dimen;
|
import com.android.systemui.plugins.ActivityStarter;
|
import com.android.systemui.qs.TouchAnimator.Builder;
|
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.UserInfoController;
|
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
|
import com.android.systemui.tuner.TunerService;
|
|
import javax.inject.Inject;
|
import javax.inject.Named;
|
|
public class QSFooterImpl extends FrameLayout implements QSFooter,
|
OnClickListener, OnUserInfoChangedListener {
|
|
private static final String TAG = "QSFooterImpl";
|
|
private final ActivityStarter mActivityStarter;
|
private final UserInfoController mUserInfoController;
|
private final DeviceProvisionedController mDeviceProvisionedController;
|
private SettingsButton mSettingsButton;
|
protected View mSettingsContainer;
|
private PageIndicator mPageIndicator;
|
|
private boolean mQsDisabled;
|
private QSPanel mQsPanel;
|
|
private boolean mExpanded;
|
|
private boolean mListening;
|
|
protected MultiUserSwitch mMultiUserSwitch;
|
private ImageView mMultiUserAvatar;
|
|
protected TouchAnimator mFooterAnimator;
|
private float mExpansionAmount;
|
|
protected View mEdit;
|
protected View mEditContainer;
|
private TouchAnimator mSettingsCogAnimator;
|
|
private View mActionsContainer;
|
private View mDragHandle;
|
|
private OnClickListener mExpandClickListener;
|
|
private final ContentObserver mDeveloperSettingsObserver = new ContentObserver(
|
new Handler(mContext.getMainLooper())) {
|
@Override
|
public void onChange(boolean selfChange, Uri uri) {
|
super.onChange(selfChange, uri);
|
setBuildText();
|
}
|
};
|
|
@Inject
|
public QSFooterImpl(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
|
ActivityStarter activityStarter, UserInfoController userInfoController,
|
DeviceProvisionedController deviceProvisionedController) {
|
super(context, attrs);
|
mActivityStarter = activityStarter;
|
mUserInfoController = userInfoController;
|
mDeviceProvisionedController = deviceProvisionedController;
|
}
|
|
@VisibleForTesting
|
public QSFooterImpl(Context context, AttributeSet attrs) {
|
this(context, attrs,
|
Dependency.get(ActivityStarter.class),
|
Dependency.get(UserInfoController.class),
|
Dependency.get(DeviceProvisionedController.class));
|
}
|
|
@Override
|
protected void onFinishInflate() {
|
super.onFinishInflate();
|
mEdit = findViewById(android.R.id.edit);
|
mEdit.setOnClickListener(view ->
|
mActivityStarter.postQSRunnableDismissingKeyguard(() ->
|
mQsPanel.showEdit(view)));
|
|
mPageIndicator = findViewById(R.id.footer_page_indicator);
|
|
mSettingsButton = findViewById(R.id.settings_button);
|
mSettingsContainer = findViewById(R.id.settings_button_container);
|
mSettingsButton.setOnClickListener(this);
|
|
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
|
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
|
|
mDragHandle = findViewById(R.id.qs_drag_handle_view);
|
mActionsContainer = findViewById(R.id.qs_footer_actions_container);
|
mEditContainer = findViewById(R.id.qs_footer_actions_edit_container);
|
|
// 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);
|
|
updateResources();
|
|
addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
|
oldBottom) -> updateAnimator(right - left));
|
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
|
updateEverything();
|
setBuildText();
|
}
|
|
private void setBuildText() {
|
TextView v = findViewById(R.id.build);
|
if (v == null) return;
|
if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
|
v.setText(mContext.getString(
|
com.android.internal.R.string.bugreport_status,
|
Build.VERSION.RELEASE,
|
Build.ID));
|
v.setVisibility(View.VISIBLE);
|
} else {
|
v.setVisibility(View.GONE);
|
}
|
}
|
|
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);
|
|
mSettingsCogAnimator = new Builder()
|
.addFloat(mSettingsContainer, "translationX",
|
isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
|
.addFloat(mSettingsButton, "rotation", -120, 0)
|
.build();
|
|
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() {
|
updateFooterAnimator();
|
}
|
|
private void updateFooterAnimator() {
|
mFooterAnimator = createFooterAnimator();
|
}
|
|
@Nullable
|
private TouchAnimator createFooterAnimator() {
|
return new TouchAnimator.Builder()
|
.addFloat(mActionsContainer, "alpha", 0, 1)
|
.addFloat(mEditContainer, "alpha", 0, 1)
|
.addFloat(mDragHandle, "alpha", 1, 0, 0)
|
.addFloat(mPageIndicator, "alpha", 0, 1)
|
.setStartDelay(0.15f)
|
.build();
|
}
|
|
@Override
|
public void setKeyguardShowing(boolean keyguardShowing) {
|
setExpansion(mExpansionAmount);
|
}
|
|
@Override
|
public void setExpandClickListener(OnClickListener onClickListener) {
|
mExpandClickListener = onClickListener;
|
}
|
|
@Override
|
public void setExpanded(boolean expanded) {
|
if (mExpanded == expanded) return;
|
mExpanded = expanded;
|
updateEverything();
|
}
|
|
@Override
|
public void setExpansion(float headerExpansionFraction) {
|
mExpansionAmount = headerExpansionFraction;
|
if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction);
|
|
if (mFooterAnimator != null) {
|
mFooterAnimator.setPosition(headerExpansionFraction);
|
}
|
}
|
|
@Override
|
protected void onAttachedToWindow() {
|
super.onAttachedToWindow();
|
mContext.getContentResolver().registerContentObserver(
|
Settings.Global.getUriFor(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED), false,
|
mDeveloperSettingsObserver, UserHandle.USER_ALL);
|
}
|
|
@Override
|
@VisibleForTesting
|
public void onDetachedFromWindow() {
|
setListening(false);
|
mContext.getContentResolver().unregisterContentObserver(mDeveloperSettingsObserver);
|
super.onDetachedFromWindow();
|
}
|
|
@Override
|
public void setListening(boolean listening) {
|
if (listening == mListening) {
|
return;
|
}
|
mListening = listening;
|
updateListeners();
|
}
|
|
@Override
|
public boolean performAccessibilityAction(int action, Bundle arguments) {
|
if (action == AccessibilityNodeInfo.ACTION_EXPAND) {
|
if (mExpandClickListener != null) {
|
mExpandClickListener.onClick(null);
|
return true;
|
}
|
}
|
return super.performAccessibilityAction(action, arguments);
|
}
|
|
@Override
|
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
super.onInitializeAccessibilityNodeInfo(info);
|
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
|
}
|
|
@Override
|
public void disable(int state1, int state2, boolean animate) {
|
final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
|
if (disabled == mQsDisabled) return;
|
mQsDisabled = disabled;
|
updateEverything();
|
}
|
|
public void updateEverything() {
|
post(() -> {
|
updateVisibilities();
|
updateClickabilities();
|
setClickable(false);
|
});
|
}
|
|
private void updateClickabilities() {
|
mMultiUserSwitch.setClickable(mMultiUserSwitch.getVisibility() == View.VISIBLE);
|
mEdit.setClickable(mEdit.getVisibility() == View.VISIBLE);
|
mSettingsButton.setClickable(mSettingsButton.getVisibility() == View.VISIBLE);
|
}
|
|
private void updateVisibilities() {
|
mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
|
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
|
TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
|
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
|
mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.INVISIBLE);
|
mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
|
mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
|
}
|
|
private boolean showUserSwitcher() {
|
return mExpanded && mMultiUserSwitch.isMultiUserEnabled();
|
}
|
|
private void updateListeners() {
|
if (mListening) {
|
mUserInfoController.addCallback(this);
|
} else {
|
mUserInfoController.removeCallback(this);
|
}
|
}
|
|
@Override
|
public void setQSPanel(final QSPanel qsPanel) {
|
mQsPanel = qsPanel;
|
if (mQsPanel != null) {
|
mMultiUserSwitch.setQsPanel(qsPanel);
|
mQsPanel.setFooterPageIndicator(mPageIndicator);
|
}
|
}
|
|
@Override
|
public void onClick(View v) {
|
// Don't do anything until view are unhidden
|
if (!mExpanded) {
|
return;
|
}
|
|
if (v == mSettingsButton) {
|
if (!mDeviceProvisionedController.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()) {
|
mActivityStarter.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();
|
}
|
}
|
}
|
|
private void startSettingsActivity() {
|
mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
|
true /* dismissShade */);
|
}
|
|
@Override
|
public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
|
if (picture != null &&
|
UserManager.get(mContext).isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) &&
|
!(picture instanceof UserIconDrawable)) {
|
picture = picture.getConstantState().newDrawable(mContext.getResources()).mutate();
|
picture.setColorFilter(
|
Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground),
|
Mode.SRC_IN);
|
}
|
mMultiUserAvatar.setImageDrawable(picture);
|
}
|
}
|