diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 436188a83d4f1..0822947e8b162 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -16,7 +16,7 @@
-->
-
-
+
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
index 2be8a9704e1c7..cfcceb2b2951a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
@@ -76,7 +76,7 @@ public class QSDetail extends LinearLayout {
private int mOpenY;
private boolean mAnimatingOpen;
private boolean mSwitchState;
- private View mFooter;
+ private QSFooter mFooter;
public QSDetail(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -120,7 +120,8 @@ public class QSDetail extends LinearLayout {
mDetailDoneButton.setOnClickListener(doneListener);
}
- public void setQsPanel(QSPanel panel, QuickStatusBarHeader header, View footer) {
+ /** */
+ public void setQsPanel(QSPanel panel, QuickStatusBarHeader header, QSFooter footer) {
mQsPanel = panel;
mHeader = header;
mFooter = footer;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
similarity index 61%
rename from packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
rename to packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 84563a0784472..8b9dae14c8091 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -11,19 +11,14 @@
* 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
+ * 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.ClipData;
-import android.content.ClipboardManager;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.graphics.PorterDuff.Mode;
@@ -36,50 +31,27 @@ import android.os.Handler;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.text.TextUtils;
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.settings.UserTracker;
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 final UserTracker mUserTracker;
+/** */
+public class QSFooterView extends FrameLayout {
private SettingsButton mSettingsButton;
protected View mSettingsContainer;
private PageIndicator mPageIndicator;
@@ -87,7 +59,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
private boolean mShouldShowBuildText;
private boolean mQsDisabled;
- private QSPanel mQsPanel;
private QuickQSPanel mQuickQsPanel;
private boolean mExpanded;
@@ -117,39 +88,19 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
}
};
- @Inject
- public QSFooterImpl(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- ActivityStarter activityStarter, UserInfoController userInfoController,
- DeviceProvisionedController deviceProvisionedController, UserTracker userTracker) {
+ public QSFooterView(Context context, AttributeSet attrs) {
super(context, attrs);
- mActivityStarter = activityStarter;
- mUserInfoController = userInfoController;
- mDeviceProvisionedController = deviceProvisionedController;
- mUserTracker = userTracker;
- }
-
- @VisibleForTesting
- public QSFooterImpl(Context context, AttributeSet attrs) {
- this(context, attrs,
- Dependency.get(ActivityStarter.class),
- Dependency.get(UserInfoController.class),
- Dependency.get(DeviceProvisionedController.class),
- Dependency.get(UserTracker.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);
@@ -157,19 +108,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
mActionsContainer = findViewById(R.id.qs_footer_actions_container);
mEditContainer = findViewById(R.id.qs_footer_actions_edit_container);
mBuildText = findViewById(R.id.build);
- mBuildText.setOnLongClickListener(view -> {
- CharSequence buildText = mBuildText.getText();
- if (!TextUtils.isEmpty(buildText)) {
- ClipboardManager service =
- mUserTracker.getUserContext().getSystemService(ClipboardManager.class);
- String label = mContext.getString(R.string.build_number_clip_data_label);
- service.setPrimaryClip(ClipData.newPlainText(label, buildText));
- Toast.makeText(mContext, R.string.build_number_copy_toast, Toast.LENGTH_SHORT)
- .show();
- return true;
- }
- return false;
- });
// RenderThread is doing more harm than good when touching the header (to expand quick
// settings), so disable it for this view
@@ -180,7 +118,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight,
oldBottom) -> updateAnimator(right - left));
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- updateEverything();
setBuildText();
}
@@ -249,24 +186,22 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
.build();
}
- @Override
- public void setKeyguardShowing(boolean keyguardShowing) {
+ /** */
+ public void setKeyguardShowing() {
setExpansion(mExpansionAmount);
}
- @Override
public void setExpandClickListener(OnClickListener onClickListener) {
mExpandClickListener = onClickListener;
}
- @Override
- public void setExpanded(boolean expanded) {
+ void setExpanded(boolean expanded, boolean isTunerEnabled) {
if (mExpanded == expanded) return;
mExpanded = expanded;
- updateEverything();
+ updateEverything(isTunerEnabled);
}
- @Override
+ /** */
public void setExpansion(float headerExpansionFraction) {
mExpansionAmount = headerExpansionFraction;
if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction);
@@ -287,18 +222,16 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
@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
@@ -318,17 +251,16 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
}
- @Override
- public void disable(int state1, int state2, boolean animate) {
+ void disable(int state2, boolean isTunerEnabled) {
final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
if (disabled == mQsDisabled) return;
mQsDisabled = disabled;
- updateEverything();
+ updateEverything(isTunerEnabled);
}
- public void updateEverything() {
+ void updateEverything(boolean isTunerEnabled) {
post(() -> {
- updateVisibilities();
+ updateVisibilities(isTunerEnabled);
updateClickabilities();
setClickable(false);
});
@@ -341,11 +273,10 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
mBuildText.setLongClickable(mBuildText.getVisibility() == View.VISIBLE);
}
- private void updateVisibilities() {
+ private void updateVisibilities(boolean isTunerEnabled) {
mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
- TunerService.isTunerEnabled(mContext, mUserTracker.getUserHandle()) ? View.VISIBLE
- : View.INVISIBLE);
+ isTunerEnabled ? 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);
@@ -358,78 +289,22 @@ public class QSFooterImpl extends FrameLayout implements QSFooter,
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) {
+ if (qsPanel != null) {
mMultiUserSwitch.setQsPanel(qsPanel);
- mQsPanel.setFooterPageIndicator(mPageIndicator);
+ qsPanel.setFooterPageIndicator(mPageIndicator);
}
}
- @Override
public void setQQSPanel(@Nullable QuickQSPanel panel) {
mQuickQsPanel = panel;
}
- @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, mUserTracker.getUserHandle())) {
- TunerService.showResetRequest(mContext, mUserTracker.getUserHandle(),
- () -> {
- // Relaunch settings so that the tuner disappears.
- startSettingsActivity();
- });
- } else {
- Toast.makeText(getContext(), R.string.tuner_toast,
- Toast.LENGTH_LONG).show();
- TunerService.setTunerEnabled(mContext, mUserTracker.getUserHandle(), 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();
+ void onUserInfoChanged(Drawable picture, boolean isGuestUser) {
+ if (picture != null && isGuestUser && !(picture instanceof UserIconDrawable)) {
+ picture = picture.getConstantState().newDrawable(getResources()).mutate();
picture.setColorFilter(
Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground),
Mode.SRC_IN);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
new file mode 100644
index 0000000000000..e3af04bdc31e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2020 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 android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.UserManager;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.qs.dagger.QSScope;
+import com.android.systemui.settings.UserTracker;
+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.tuner.TunerService;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+
+/**
+ * Controller for {@link QSFooterView}.
+ */
+@QSScope
+public class QSFooterViewController extends ViewController implements QSFooter {
+
+ private final UserManager mUserManager;
+ private final UserInfoController mUserInfoController;
+ private final ActivityStarter mActivityStarter;
+ private final DeviceProvisionedController mDeviceProvisionedController;
+ private final UserTracker mUserTracker;
+ private final QSPanelController mQsPanelController;
+ private final TunerService mTunerService;
+ private final MetricsLogger mMetricsLogger;
+ private final SettingsButton mSettingsButton;
+ private final TextView mBuildText;
+ private final View mEdit;
+
+ private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
+ new UserInfoController.OnUserInfoChangedListener() {
+ @Override
+ public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
+ boolean isGuestUser = mUserManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser());
+ mView.onUserInfoChanged(picture, isGuestUser);
+ }
+ };
+
+ private final View.OnClickListener mSettingsOnClickListener = new View.OnClickListener() {
+ @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;
+ }
+ mMetricsLogger.action(
+ mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
+ : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
+ if (mSettingsButton.isTunerClick()) {
+ mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
+ if (isTunerEnabled()) {
+ mTunerService.showResetRequest(
+ mUserTracker.getUserHandle(),
+ () -> {
+ // Relaunch settings so that the tuner disappears.
+ startSettingsActivity();
+ });
+ } else {
+ Toast.makeText(getContext(), R.string.tuner_toast,
+ Toast.LENGTH_LONG).show();
+ mTunerService.setTunerEnabled(mUserTracker.getUserHandle(), true);
+ }
+ startSettingsActivity();
+
+ });
+ } else {
+ startSettingsActivity();
+ }
+ }
+ }
+ };
+
+ private boolean mListening;
+ private boolean mExpanded;
+
+ @Inject
+ QSFooterViewController(QSFooterView view, UserManager userManager,
+ UserInfoController userInfoController, ActivityStarter activityStarter,
+ DeviceProvisionedController deviceProvisionedController, UserTracker userTracker,
+ QSPanelController qsPanelController, TunerService tunerService,
+ MetricsLogger metricsLogger) {
+ super(view);
+ mUserManager = userManager;
+ mUserInfoController = userInfoController;
+ mActivityStarter = activityStarter;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mUserTracker = userTracker;
+ mQsPanelController = qsPanelController;
+ mTunerService = tunerService;
+ mMetricsLogger = metricsLogger;
+
+ mSettingsButton = mView.findViewById(R.id.settings_button);
+ mBuildText = mView.findViewById(R.id.build);
+ mEdit = mView.findViewById(android.R.id.edit);
+ }
+
+
+ @Override
+ protected void onViewAttached() {
+ mSettingsButton.setOnClickListener(mSettingsOnClickListener);
+ mBuildText.setOnLongClickListener(view -> {
+ CharSequence buildText = mBuildText.getText();
+ if (!TextUtils.isEmpty(buildText)) {
+ ClipboardManager service =
+ mUserTracker.getUserContext().getSystemService(ClipboardManager.class);
+ String label = getResources().getString(R.string.build_number_clip_data_label);
+ service.setPrimaryClip(ClipData.newPlainText(label, buildText));
+ Toast.makeText(getContext(), R.string.build_number_copy_toast, Toast.LENGTH_SHORT)
+ .show();
+ return true;
+ }
+ return false;
+ });
+
+ mEdit.setOnClickListener(view ->
+ mActivityStarter.postQSRunnableDismissingKeyguard(() ->
+ mQsPanelController.showEdit(view)));
+
+ mView.updateEverything(isTunerEnabled());
+ }
+
+ @Override
+ protected void onViewDetached() {
+ setListening(false);
+ }
+
+
+ @Override
+ public void setQSPanel(@Nullable QSPanel panel) {
+ mView.setQSPanel(panel);
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ mView.setVisibility(visibility);
+ }
+
+ @Override
+ public void setExpanded(boolean expanded) {
+ mExpanded = expanded;
+ mView.setExpanded(expanded, isTunerEnabled());
+ }
+
+
+ @Override
+ public int getHeight() {
+ return mView.getHeight();
+ }
+
+ @Override
+ public void setExpansion(float expansion) {
+ mView.setExpansion(expansion);
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+ if (mListening == listening) {
+ return;
+ }
+
+ mListening = listening;
+ if (mListening) {
+ mUserInfoController.addCallback(mOnUserInfoChangedListener);
+ } else {
+ mUserInfoController.removeCallback(mOnUserInfoChangedListener);
+ }
+ }
+
+ @Override
+ public void setKeyguardShowing(boolean keyguardShowing) {
+ mView.setKeyguardShowing();
+ }
+
+ /** */
+ @Override
+ public void setExpandClickListener(View.OnClickListener onClickListener) {
+ mView.setExpandClickListener(onClickListener);
+ }
+
+ @Override
+ public void setQQSPanel(@Nullable QuickQSPanel panel) {
+ mView.setQQSPanel(panel);
+ }
+
+ @Override
+ public void disable(int state1, int state2, boolean animate) {
+ mView.disable(state2, isTunerEnabled());
+ }
+
+
+ private void startSettingsActivity() {
+ mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
+ true /* dismissShade */);
+ }
+
+ private boolean isTunerEnabled() {
+ return mTunerService.isTunerEnabled(mUserTracker.getUserHandle());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 4ea600ab5e255..1a7d366d84b40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -142,13 +142,13 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mQSDetail = view.findViewById(R.id.qs_detail);
mHeader = view.findViewById(R.id.header);
mQSPanelController.setHeaderContainer(view.findViewById(R.id.header_text_container));
- mFooter = view.findViewById(R.id.qs_footer);
+ mFooter = qsFragmentComponent.getQSFooter();
mQSContainerImplController = qsFragmentComponent.getQSContainerImplController();
mQSContainerImplController.init();
mContainer = mQSContainerImplController.getView();
- mQSDetail.setQsPanel(mQSPanelController.getView(), mHeader, (View) mFooter);
+ mQSDetail.setQsPanel(mQSPanelController.getView(), mHeader, mFooter);
mQSAnimator = qsFragmentComponent.getQSAnimator();
mQSCustomizer = view.findViewById(R.id.qs_customize);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 4e2351f66ca25..f222b0d1ccc01 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -20,6 +20,7 @@ import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
import android.annotation.NonNull;
import android.content.res.Configuration;
+import android.view.View;
import android.view.ViewGroup;
import com.android.internal.logging.MetricsLogger;
@@ -192,4 +193,9 @@ public class QSPanelController extends QSPanelControllerBase {
super.refreshAllTiles();
mQsSecurityFooter.refreshState();
}
+
+ /** Start customizing the Quick Settings. */
+ public void showEdit(View view) {
+ mView.showEdit(view);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
index 7ba3563f3b201..51b2c8dfffbd9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -18,6 +18,7 @@ package com.android.systemui.qs.dagger;
import com.android.systemui.qs.QSAnimator;
import com.android.systemui.qs.QSContainerImplController;
+import com.android.systemui.qs.QSFooter;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.qs.QuickQSPanelController;
@@ -48,4 +49,7 @@ public interface QSFragmentComponent {
/** Construct a {@link QSContainerImplController}. */
QSContainerImplController getQSContainerImplController();
+
+ /** Construct a {@link QSFooter} */
+ QSFooter getQSFooter();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index ee3f2f60fccc5..4bf4eff4c27e9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -22,6 +22,9 @@ import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSContainerImpl;
+import com.android.systemui.qs.QSFooter;
+import com.android.systemui.qs.QSFooterView;
+import com.android.systemui.qs.QSFooterViewController;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QuickQSPanel;
@@ -70,4 +73,18 @@ public interface QSFragmentModule {
static QuickQSPanel providesQuickQSPanel(QuickStatusBarHeader quickStatusBarHeader) {
return quickStatusBarHeader.findViewById(R.id.quick_qs_panel);
}
+
+ /** */
+ @Provides
+ static QSFooterView providesQSFooterView(@RootView View view) {
+ return view.findViewById(R.id.qs_footer);
+ }
+
+ /** */
+ @Provides
+ @QSScope
+ static QSFooter providesQSFooter(QSFooterViewController qsFooterViewController) {
+ qsFooterViewController.init();
+ return qsFooterViewController;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 70bba263ab90c..b67574d1c4dee 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog;
public abstract class TunerService {
public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
+ private final Context mContext;
public abstract void clearAll();
public abstract void destroy();
@@ -50,6 +51,10 @@ public abstract class TunerService {
void onTuningChanged(String key, String newValue);
}
+ public TunerService(Context context) {
+ mContext = context;
+ }
+
private static Context userContext(Context context, UserHandle user) {
try {
return context.createPackageContextAsUser(context.getPackageName(), 0, user);
@@ -58,6 +63,11 @@ public abstract class TunerService {
}
}
+ /** Enables or disables the tuner for the supplied user. */
+ public void setTunerEnabled(UserHandle user, boolean enabled) {
+ setTunerEnabled(mContext, user, enabled);
+ }
+
public static final void setTunerEnabled(Context context, UserHandle user, boolean enabled) {
userContext(context, user).getPackageManager().setComponentEnabledSetting(
new ComponentName(context, TunerActivity.class),
@@ -66,6 +76,11 @@ public abstract class TunerService {
PackageManager.DONT_KILL_APP);
}
+ /** Returns true if the tuner is enabled for the supplied user. */
+ public boolean isTunerEnabled(UserHandle user) {
+ return isTunerEnabled(mContext, user);
+ }
+
public static final boolean isTunerEnabled(Context context, UserHandle user) {
return userContext(context, user).getPackageManager().getComponentEnabledSetting(
new ComponentName(context, TunerActivity.class))
@@ -81,6 +96,11 @@ public abstract class TunerService {
}
}
+ /** */
+ public void showResetRequest(UserHandle user, final Runnable onDisabled) {
+ showResetRequest(mContext, user, onDisabled);
+ }
+
public static final void showResetRequest(final Context context, UserHandle user,
final Runnable onDisabled) {
SystemUIDialog dialog = new SystemUIDialog(context);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 22f03e074b068..027c282ba3529 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -94,6 +94,7 @@ public class TunerServiceImpl extends TunerService {
DemoModeController demoModeController,
BroadcastDispatcher broadcastDispatcher,
UserTracker userTracker) {
+ super(context);
mContext = context;
mContentResolver = mContext.getContentResolver();
mLeakDetector = leakDetector;
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 344f0d2f5506f..e79d432b3b151 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -24,7 +24,6 @@ import android.view.LayoutInflater;
import android.view.View;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.qs.QSFooterImpl;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.customize.QSCustomizer;
@@ -91,11 +90,6 @@ public class InjectionInflationController {
@BindsInstance AttributeSet attributeSet);
}
- /**
- * Creates the QSFooterImpl.
- */
- QSFooterImpl createQsFooter();
-
/**
* Creates the NotificationStackScrollLayout.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
index c7aa780fcacb5..3dbc6f101a909 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
@@ -16,6 +16,8 @@
package com.android.systemui.util;
+import android.content.Context;
+import android.content.res.Resources;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
@@ -75,6 +77,14 @@ public abstract class ViewController {
}
}
+ protected Context getContext() {
+ return mView.getContext();
+ }
+
+ protected Resources getResources() {
+ return mView.getResources();
+ }
+
/**
* Called when the view is attached and a call to {@link #init()} has been made in either order.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
index 353efeeb21f60..803919200150b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
@@ -70,7 +70,7 @@ public class QSDetailTest extends SysuiTestCase {
mQsDetail = (QSDetail) LayoutInflater.from(mContext).inflate(R.layout.qs_detail, null);
mQsPanel = mock(QSPanel.class);
mQuickHeader = mock(QuickStatusBarHeader.class);
- mQsDetail.setQsPanel(mQsPanel, mQuickHeader, mock(View.class));
+ mQsDetail.setQsPanel(mQsPanel, mQuickHeader, mock(QSFooter.class));
mMockDetailAdapter = mock(DetailAdapter.class);
when(mMockDetailAdapter.createDetailView(any(), any(), any()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
deleted file mode 100644
index 99f2d80425470..0000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.R;
-import com.android.systemui.R.id;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class QSFooterImplTest extends LeakCheckedTest {
-
- private QSFooterImpl mFooter;
- private ActivityStarter mActivityStarter;
- private DeviceProvisionedController mDeviceProvisionedController;
- private UserInfoController mUserInfoController;
- private UserTracker mUserTracker;
- @Mock
- private ClipboardManager mClipboardManager;
-
- @Before
- public void setup() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
- mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
- mDeviceProvisionedController = mDependency.injectMockDependency(
- DeviceProvisionedController.class);
- mUserInfoController = mDependency.injectMockDependency(UserInfoController.class);
- mUserTracker = mDependency.injectMockDependency(UserTracker.class);
-
- mContext.addMockSystemService(ClipboardManager.class, mClipboardManager);
-
- when(mUserTracker.getUserContext()).thenReturn(mContext);
-
- TestableLooper.get(this).runWithLooper(
- () -> mFooter = (QSFooterImpl) LayoutInflater.from(mContext).inflate(
- R.layout.qs_footer_impl, null));
- }
-
- @Test
- public void testBuildTextCopy() {
- TextView buildTextView = mFooter.requireViewById(R.id.build);
- CharSequence buildText = "TEST";
- buildTextView.setText(buildText);
- buildTextView.setLongClickable(true);
-
- buildTextView.performLongClick();
-
- ArgumentCaptor captor = ArgumentCaptor.forClass(ClipData.class);
- verify(mClipboardManager).setPrimaryClip(captor.capture());
- assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(buildText);
- }
-
- @Test
- @Ignore("failing")
- public void testSettings_UserNotSetup() {
- View settingsButton = mFooter.findViewById(id.settings_button);
- when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
-
- mFooter.onClick(settingsButton);
- // Verify Settings wasn't launched.
- verify(mActivityStarter, never()).startActivity(any(), anyBoolean());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
new file mode 100644
index 0000000000000..065f236d19b32
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2020 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.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.os.UserManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.settings.UserTracker;
+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.tuner.TunerService;
+import com.android.systemui.utils.leaks.FakeTunerService;
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class QSFooterViewControllerTest extends LeakCheckedTest {
+
+ @Mock
+ private QSFooterView mView;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private DeviceProvisionedController mDeviceProvisionedController;
+ @Mock
+ private UserInfoController mUserInfoController;
+ @Mock
+ private UserTracker mUserTracker;
+ @Mock
+ private QSPanelController mQSPanelController;
+ @Mock
+ private ClipboardManager mClipboardManager;
+ private FakeTunerService mFakeTunerService;
+ private MetricsLogger mMetricsLogger = new FakeMetricsLogger();
+
+ @Mock
+ private SettingsButton mSettingsButton;
+ @Mock
+ private TextView mBuildText;
+ @Mock
+ private View mEdit;
+
+ private QSFooterViewController mController;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
+
+ mFakeTunerService = (FakeTunerService) Dependency.get(TunerService.class);
+
+ mContext.addMockSystemService(ClipboardManager.class, mClipboardManager);
+
+ when(mView.getContext()).thenReturn(mContext);
+ when(mView.getResources()).thenReturn(mContext.getResources());
+ when(mUserTracker.getUserContext()).thenReturn(mContext);
+
+ when(mView.isAttachedToWindow()).thenReturn(true);
+ when(mView.findViewById(R.id.settings_button)).thenReturn(mSettingsButton);
+ when(mView.findViewById(R.id.build)).thenReturn(mBuildText);
+ when(mView.findViewById(android.R.id.edit)).thenReturn(mEdit);
+
+ mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
+ mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
+ mFakeTunerService, mMetricsLogger);
+
+ mController.init();
+ }
+
+ @Test
+ public void testBuildTextCopy() {
+ String text = "TEST";
+ ArgumentCaptor onLongClickCaptor =
+ ArgumentCaptor.forClass(View.OnLongClickListener.class);
+
+ verify(mBuildText).setOnLongClickListener(onLongClickCaptor.capture());
+
+ when(mBuildText.getText()).thenReturn(text);
+ onLongClickCaptor.getValue().onLongClick(mBuildText);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ClipData.class);
+ verify(mClipboardManager).setPrimaryClip(captor.capture());
+ assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(text);
+ }
+
+ @Test
+ public void testSettings_UserNotSetup() {
+ ArgumentCaptor onClickCaptor =
+ ArgumentCaptor.forClass(View.OnClickListener.class);
+ verify(mSettingsButton).setOnClickListener(onClickCaptor.capture());
+
+ when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
+
+ onClickCaptor.getValue().onClick(mSettingsButton);
+ // Verify Settings wasn't launched.
+ verify(mActivityStarter, never()).startActivity(any(), anyBoolean());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
index 8db82e2a52fc3..97d4aa7e4d19c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
@@ -14,7 +14,7 @@
package com.android.systemui.utils.leaks;
-import android.content.Context;
+import android.os.UserHandle;
import android.testing.LeakCheck;
import com.android.systemui.tuner.TunerService;
@@ -22,8 +22,10 @@ import com.android.systemui.tuner.TunerService;
public class FakeTunerService extends TunerService {
private final BaseLeakChecker mBaseLeakChecker;
+ private boolean mEnabled;
public FakeTunerService(LeakCheck test) {
+ super(null);
mBaseLeakChecker = new BaseLeakChecker<>(test, "tunable");
}
@@ -74,4 +76,14 @@ public class FakeTunerService extends TunerService {
public void setValue(String setting, int value) {
}
+
+ @Override
+ public void setTunerEnabled(UserHandle user, boolean enabled) {
+ mEnabled = enabled;
+ }
+
+ @Override
+ public boolean isTunerEnabled(UserHandle user) {
+ return mEnabled;
+ }
}