From 6863447c625bf99e11abbeaacf4466be1f4e2467 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Wed, 14 Oct 2020 10:42:39 -0400 Subject: [PATCH 1/5] 1/N Add Controller for QSFooter Not much too this CL. It adds QSFooterImplController, has it implementing QSFooter, and then uses the controller anywhere the view was being used before. Bug: 168904199 Test: manual Change-Id: Ie1d5125c8cd706e325a40f4b60a52bab1dd3d213 --- .../src/com/android/systemui/qs/QSDetail.java | 5 +- .../systemui/qs/QSFooterImplController.java | 111 ++++++++++++++++++ .../com/android/systemui/qs/QSFragment.java | 5 +- .../qs/dagger/QSFragmentComponent.java | 4 + 4 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java 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/QSFooterImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java new file mode 100644 index 0000000000000..7b51c2b0a4d87 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java @@ -0,0 +1,111 @@ +/* + * 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.view.View; + +import androidx.annotation.Nullable; + +import com.android.systemui.qs.dagger.QSScope; +import com.android.systemui.util.ViewController; + +import javax.inject.Inject; + +/** + * Controller for {@link QSFooterImpl}. + */ +@QSScope +public class QSFooterImplController extends ViewController implements QSFooter { + @Inject + QSFooterImplController(QSFooterImpl view) { + super(view); + } + + @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) { + mView.setExpanded(expanded); + } + + @Override + public int getHeight() { + return mView.getHeight(); + } + + @Override + public void setExpansion(float expansion) { + mView.setExpansion(expansion); + } + + @Override + public void setListening(boolean listening) { + mView.setListening(listening); + } + + @Override + public void setKeyguardShowing(boolean keyguardShowing) { + mView.setKeyguardShowing(keyguardShowing); + } + + @Override + public void setExpandClickListener(View.OnClickListener onClickListener) { + mView.setExpandClickListener(onClickListener); + } + + @Override + protected void onViewAttached() { + } + + @Override + protected void onViewDetached() { + } + + @Override + public void setQQSPanel(@Nullable QuickQSPanel panel) { + mView.setQQSPanel(panel); + } + + @Override + public void disable(int state1, int state2, boolean animate) { + mView.disable(state1, state2, animate); + } + + /** + * Factory for {@link QSFooterImplController}. + * + * TODO(b/168904199): Delete this once QSFooterImpl is no longer marked as injectable. + */ + @QSScope + public static class Factory { + @Inject + Factory() { + } + + QSFooterImplController create(QSFooterImpl view) { + return new QSFooterImplController(view); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 4ea600ab5e255..ca91adba33640 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -142,13 +142,14 @@ 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.getQSFooterFactory().create( + view.findViewById(R.id.qs_footer)); 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/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java index 7ba3563f3b201..59b9be62da129 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.QSFooterImplController; 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(); + + /** */ + QSFooterImplController.Factory getQSFooterFactory(); } From ffbae7f39a8da40d244f99a2b3929e21c9509cd3 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Wed, 14 Oct 2020 11:09:40 -0400 Subject: [PATCH 2/5] 2/N Move UserInfoController out of QSFooterImpl Bug: 168904199 Test: manual && atest SystemUITests Change-Id: I2e409ec2fad4b9167a1900571dc30eff01affde5 --- .../com/android/systemui/qs/QSFooterImpl.java | 39 ++--------- .../systemui/qs/QSFooterImplController.java | 64 +++++++++++++++---- .../com/android/systemui/qs/QSDetailTest.java | 2 +- 3 files changed, 58 insertions(+), 47 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index 84563a0784472..86d6e390e320d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -51,7 +51,6 @@ 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; @@ -64,20 +63,17 @@ 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 { +/** */ +public class QSFooterImpl extends FrameLayout implements OnClickListener { private static final String TAG = "QSFooterImpl"; private final ActivityStarter mActivityStarter; - private final UserInfoController mUserInfoController; private final DeviceProvisionedController mDeviceProvisionedController; private final UserTracker mUserTracker; private SettingsButton mSettingsButton; @@ -119,11 +115,10 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, @Inject public QSFooterImpl(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - ActivityStarter activityStarter, UserInfoController userInfoController, + ActivityStarter activityStarter, DeviceProvisionedController deviceProvisionedController, UserTracker userTracker) { super(context, attrs); mActivityStarter = activityStarter; - mUserInfoController = userInfoController; mDeviceProvisionedController = deviceProvisionedController; mUserTracker = userTracker; } @@ -132,7 +127,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, 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)); } @@ -249,24 +243,20 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, .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); @@ -287,18 +277,15 @@ 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,7 +305,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, 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; @@ -358,15 +344,6 @@ 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) { @@ -375,7 +352,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, } } - @Override public void setQQSPanel(@Nullable QuickQSPanel panel) { mQuickQsPanel = panel; } @@ -424,12 +400,9 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, 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/QSFooterImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java index 7b51c2b0a4d87..e678f911f468c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java @@ -16,11 +16,15 @@ package com.android.systemui.qs; +import android.graphics.drawable.Drawable; +import android.os.UserManager; import android.view.View; import androidx.annotation.Nullable; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.qs.dagger.QSScope; +import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.util.ViewController; import javax.inject.Inject; @@ -30,11 +34,40 @@ import javax.inject.Inject; */ @QSScope public class QSFooterImplController extends ViewController implements QSFooter { + + private final UserManager mUserManager; + private final UserInfoController mUserInfoController; + + 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 boolean mListening; + @Inject - QSFooterImplController(QSFooterImpl view) { + QSFooterImplController(QSFooterImpl view, UserManager userManager, + UserInfoController userInfoController) { super(view); + mUserManager = userManager; + mUserInfoController = userInfoController; } + + @Override + protected void onViewAttached() { + } + + @Override + protected void onViewDetached() { + setListening(false); + } + + @Override public void setQSPanel(@Nullable QSPanel panel) { mView.setQSPanel(panel); @@ -62,7 +95,16 @@ public class QSFooterImplController extends ViewController impleme @Override public void setListening(boolean listening) { - mView.setListening(listening); + if (mListening == listening) { + return; + } + + mListening = listening; + if (mListening) { + mUserInfoController.addCallback(mOnUserInfoChangedListener); + } else { + mUserInfoController.removeCallback(mOnUserInfoChangedListener); + } } @Override @@ -74,15 +116,6 @@ public class QSFooterImplController extends ViewController impleme public void setExpandClickListener(View.OnClickListener onClickListener) { mView.setExpandClickListener(onClickListener); } - - @Override - protected void onViewAttached() { - } - - @Override - protected void onViewDetached() { - } - @Override public void setQQSPanel(@Nullable QuickQSPanel panel) { mView.setQQSPanel(panel); @@ -100,12 +133,17 @@ public class QSFooterImplController extends ViewController impleme */ @QSScope public static class Factory { + private final UserManager mUserManager; + private final UserInfoController mUserInfoController; + @Inject - Factory() { + Factory(UserManager userManager, UserInfoController userInfoController) { + mUserManager = userManager; + mUserInfoController = userInfoController; } QSFooterImplController create(QSFooterImpl view) { - return new QSFooterImplController(view); + return new QSFooterImplController(view, mUserManager, mUserInfoController); } } } 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())) From 2a89fa3359e2d76e1562eeb2f5f74cae15672ce6 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Wed, 14 Oct 2020 14:54:09 -0400 Subject: [PATCH 3/5] 3/N Remove DevProvCtrl & UserTracker from QSFooter Remove DeviceProvisionedConroller and UserTracker from QSFooterImpl. This also adds QSFooterImplControllerTest, and fixes one of the tests that was marked as failing. Bug: 168904199 Test: manual Change-Id: I0e04bef8df76c6b73c8844f4679cfef08b3b2c13 --- .../com/android/systemui/qs/QSFooterImpl.java | 106 ++----------- .../systemui/qs/QSFooterImplController.java | 133 ++++++++++++++++- .../android/systemui/tuner/TunerService.java | 20 +++ .../systemui/tuner/TunerServiceImpl.java | 1 + .../android/systemui/util/ViewController.java | 10 ++ .../qs/QSFooterImplControllerTest.java | 139 ++++++++++++++++++ .../android/systemui/qs/QSFooterImplTest.java | 109 -------------- .../utils/leaks/FakeTunerService.java | 14 +- 8 files changed, 323 insertions(+), 209 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplControllerTest.java delete mode 100644 packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index 86d6e390e320d..64e0a1ead4682 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -20,10 +20,7 @@ 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,21 +33,16 @@ 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.settingslib.Utils; import com.android.settingslib.development.DevelopmentSettingsEnabler; import com.android.settingslib.drawable.UserIconDrawable; @@ -59,23 +51,15 @@ 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.tuner.TunerService; import javax.inject.Inject; import javax.inject.Named; /** */ -public class QSFooterImpl extends FrameLayout implements OnClickListener { - - private static final String TAG = "QSFooterImpl"; - +public class QSFooterImpl extends FrameLayout { private final ActivityStarter mActivityStarter; - private final DeviceProvisionedController mDeviceProvisionedController; - private final UserTracker mUserTracker; private SettingsButton mSettingsButton; protected View mSettingsContainer; private PageIndicator mPageIndicator; @@ -115,20 +99,14 @@ public class QSFooterImpl extends FrameLayout implements OnClickListener { @Inject public QSFooterImpl(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - ActivityStarter activityStarter, - DeviceProvisionedController deviceProvisionedController, UserTracker userTracker) { + ActivityStarter activityStarter) { super(context, attrs); mActivityStarter = activityStarter; - mDeviceProvisionedController = deviceProvisionedController; - mUserTracker = userTracker; } @VisibleForTesting public QSFooterImpl(Context context, AttributeSet attrs) { - this(context, attrs, - Dependency.get(ActivityStarter.class), - Dependency.get(DeviceProvisionedController.class), - Dependency.get(UserTracker.class)); + this(context, attrs, Dependency.get(ActivityStarter.class)); } @Override @@ -143,7 +121,6 @@ public class QSFooterImpl extends FrameLayout implements OnClickListener { 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); @@ -151,19 +128,6 @@ public class QSFooterImpl extends FrameLayout implements OnClickListener { 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 @@ -174,7 +138,6 @@ public class QSFooterImpl extends FrameLayout implements OnClickListener { addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> updateAnimator(right - left)); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); - updateEverything(); setBuildText(); } @@ -243,7 +206,8 @@ public class QSFooterImpl extends FrameLayout implements OnClickListener { .build(); } - public void setKeyguardShowing(boolean keyguardShowing) { + /** */ + public void setKeyguardShowing() { setExpansion(mExpansionAmount); } @@ -251,10 +215,10 @@ public class QSFooterImpl extends FrameLayout implements OnClickListener { mExpandClickListener = onClickListener; } - public void setExpanded(boolean expanded) { + void setExpanded(boolean expanded, boolean isTunerEnabled) { if (mExpanded == expanded) return; mExpanded = expanded; - updateEverything(); + updateEverything(isTunerEnabled); } public void setExpansion(float headerExpansionFraction) { @@ -305,16 +269,16 @@ public class QSFooterImpl extends FrameLayout implements OnClickListener { info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND); } - 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); }); @@ -327,11 +291,10 @@ public class QSFooterImpl extends FrameLayout implements OnClickListener { 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); @@ -356,49 +319,6 @@ public class QSFooterImpl extends FrameLayout implements OnClickListener { 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 */); - } void onUserInfoChanged(Drawable picture, boolean isGuestUser) { if (picture != null && isGuestUser && !(picture instanceof UserIconDrawable)) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java index e678f911f468c..03b2cf48c7f4c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java @@ -16,15 +16,29 @@ 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; @@ -37,6 +51,13 @@ public class QSFooterImplController extends ViewController impleme private final UserManager mUserManager; private final UserInfoController mUserInfoController; + private final ActivityStarter mActivityStarter; + private final DeviceProvisionedController mDeviceProvisionedController; + private final UserTracker mUserTracker; + private final TunerService mTunerService; + private final MetricsLogger mMetricsLogger; + private final SettingsButton mSettingsButton; + private final TextView mBuildText; private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener = new UserInfoController.OnUserInfoChangedListener() { @@ -47,19 +68,88 @@ public class QSFooterImplController extends ViewController impleme } }; + 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 QSFooterImplController(QSFooterImpl view, UserManager userManager, - UserInfoController userInfoController) { + UserInfoController userInfoController, ActivityStarter activityStarter, + DeviceProvisionedController deviceProvisionedController, UserTracker userTracker, + TunerService tunerService, MetricsLogger metricsLogger) { super(view); mUserManager = userManager; mUserInfoController = userInfoController; + mActivityStarter = activityStarter; + mDeviceProvisionedController = deviceProvisionedController; + mUserTracker = userTracker; + mTunerService = tunerService; + mMetricsLogger = metricsLogger; + + mSettingsButton = mView.findViewById(R.id.settings_button); + mBuildText = mView.findViewById(R.id.build); } @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; + }); + + mView.updateEverything(isTunerEnabled()); } @Override @@ -80,9 +170,11 @@ public class QSFooterImplController extends ViewController impleme @Override public void setExpanded(boolean expanded) { - mView.setExpanded(expanded); + mExpanded = expanded; + mView.setExpanded(expanded, isTunerEnabled()); } + @Override public int getHeight() { return mView.getHeight(); @@ -109,13 +201,15 @@ public class QSFooterImplController extends ViewController impleme @Override public void setKeyguardShowing(boolean keyguardShowing) { - mView.setKeyguardShowing(keyguardShowing); + mView.setKeyguardShowing(); } + /** */ @Override public void setExpandClickListener(View.OnClickListener onClickListener) { mView.setExpandClickListener(onClickListener); } + @Override public void setQQSPanel(@Nullable QuickQSPanel panel) { mView.setQQSPanel(panel); @@ -123,7 +217,17 @@ public class QSFooterImplController extends ViewController impleme @Override public void disable(int state1, int state2, boolean animate) { - mView.disable(state1, state2, 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()); } /** @@ -135,15 +239,32 @@ public class QSFooterImplController extends ViewController impleme public static class Factory { private final UserManager mUserManager; private final UserInfoController mUserInfoController; + private final ActivityStarter mActivityStarter; + private final DeviceProvisionedController mDeviceProvisionedController; + private final UserTracker mUserTracker; + private final TunerService mTunerService; + private final MetricsLogger mMetricsLogger; @Inject - Factory(UserManager userManager, UserInfoController userInfoController) { + Factory(UserManager userManager, UserInfoController userInfoController, + ActivityStarter activityStarter, + DeviceProvisionedController deviceProvisionedController, UserTracker userTracker, + TunerService tunerService, MetricsLogger metricsLogger) { mUserManager = userManager; mUserInfoController = userInfoController; + mActivityStarter = activityStarter; + mDeviceProvisionedController = deviceProvisionedController; + mUserTracker = userTracker; + mTunerService = tunerService; + mMetricsLogger = metricsLogger; } QSFooterImplController create(QSFooterImpl view) { - return new QSFooterImplController(view, mUserManager, mUserInfoController); + QSFooterImplController controller = new QSFooterImplController(view, mUserManager, + mUserInfoController, mActivityStarter, mDeviceProvisionedController, + mUserTracker, mTunerService, mMetricsLogger); + controller.init(); + return controller; } } } 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/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/QSFooterImplControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplControllerTest.java new file mode 100644 index 0000000000000..293f3c901b51d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplControllerTest.java @@ -0,0 +1,139 @@ +/* + * 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 QSFooterImplControllerTest extends LeakCheckedTest { + + @Mock + private QSFooterImpl mView; + @Mock + private UserManager mUserManager; + @Mock + private ActivityStarter mActivityStarter; + @Mock + private DeviceProvisionedController mDeviceProvisionedController; + @Mock + private UserInfoController mUserInfoController; + @Mock + private UserTracker mUserTracker; + @Mock + private ClipboardManager mClipboardManager; + private FakeTunerService mFakeTunerService; + private MetricsLogger mMetricsLogger = new FakeMetricsLogger(); + + @Mock + private SettingsButton mSettingsButton; + @Mock + private TextView mBuildText; + + private QSFooterImplController 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); + + mController = new QSFooterImplController(mView, mUserManager, mUserInfoController, + mActivityStarter, mDeviceProvisionedController, mUserTracker, 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/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/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; + } } From a61f381ca98ae9e926d6d5e3b523ca6c6f874a87 Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Wed, 14 Oct 2020 16:38:36 -0400 Subject: [PATCH 4/5] 4/N Remove ActivityStarter from QSFooterImpl. This removes the last dependency injection into the QSFooterImpl view. Bug: 168904199 Test: manual && atest SystemUITests Change-Id: I1b6d811d2d21d1df99687671ad3bc6331ce3eef3 --- .../com/android/systemui/qs/QSFooterImpl.java | 27 +++---------------- .../systemui/qs/QSFooterImplController.java | 18 ++++++++++--- .../systemui/qs/QSPanelController.java | 6 +++++ .../util/InjectionInflationController.java | 6 ----- .../qs/QSFooterImplControllerTest.java | 9 +++++-- 5 files changed, 31 insertions(+), 35 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index 64e0a1ead4682..399321261340e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -18,8 +18,6 @@ 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.res.Configuration; import android.database.ContentObserver; @@ -46,20 +44,14 @@ import androidx.annotation.VisibleForTesting; 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 javax.inject.Inject; -import javax.inject.Named; - /** */ public class QSFooterImpl extends FrameLayout { - private final ActivityStarter mActivityStarter; private SettingsButton mSettingsButton; protected View mSettingsContainer; private PageIndicator mPageIndicator; @@ -67,7 +59,6 @@ public class QSFooterImpl extends FrameLayout { private boolean mShouldShowBuildText; private boolean mQsDisabled; - private QSPanel mQsPanel; private QuickQSPanel mQuickQsPanel; private boolean mExpanded; @@ -97,25 +88,14 @@ public class QSFooterImpl extends FrameLayout { } }; - @Inject - public QSFooterImpl(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - ActivityStarter activityStarter) { - super(context, attrs); - mActivityStarter = activityStarter; - } - - @VisibleForTesting public QSFooterImpl(Context context, AttributeSet attrs) { - this(context, attrs, Dependency.get(ActivityStarter.class)); + super(context, attrs); } @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); @@ -308,10 +288,9 @@ public class QSFooterImpl extends FrameLayout { } public void setQSPanel(final QSPanel qsPanel) { - mQsPanel = qsPanel; - if (mQsPanel != null) { + if (qsPanel != null) { mMultiUserSwitch.setQsPanel(qsPanel); - mQsPanel.setFooterPageIndicator(mPageIndicator); + qsPanel.setFooterPageIndicator(mPageIndicator); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java index 03b2cf48c7f4c..ddd66b9642cda 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java @@ -54,10 +54,12 @@ public class QSFooterImplController extends ViewController impleme 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() { @@ -117,18 +119,21 @@ public class QSFooterImplController extends ViewController impleme QSFooterImplController(QSFooterImpl view, UserManager userManager, UserInfoController userInfoController, ActivityStarter activityStarter, DeviceProvisionedController deviceProvisionedController, UserTracker userTracker, - TunerService tunerService, MetricsLogger metricsLogger) { + 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); } @@ -149,6 +154,10 @@ public class QSFooterImplController extends ViewController impleme return false; }); + mEdit.setOnClickListener(view -> + mActivityStarter.postQSRunnableDismissingKeyguard(() -> + mQsPanelController.showEdit(view))); + mView.updateEverything(isTunerEnabled()); } @@ -242,6 +251,7 @@ public class QSFooterImplController extends ViewController impleme private final ActivityStarter mActivityStarter; private final DeviceProvisionedController mDeviceProvisionedController; private final UserTracker mUserTracker; + private final QSPanelController mQsPanelController; private final TunerService mTunerService; private final MetricsLogger mMetricsLogger; @@ -249,12 +259,14 @@ public class QSFooterImplController extends ViewController impleme Factory(UserManager userManager, UserInfoController userInfoController, ActivityStarter activityStarter, DeviceProvisionedController deviceProvisionedController, UserTracker userTracker, - TunerService tunerService, MetricsLogger metricsLogger) { + QSPanelController qsPanelController, TunerService tunerService, + MetricsLogger metricsLogger) { mUserManager = userManager; mUserInfoController = userInfoController; mActivityStarter = activityStarter; mDeviceProvisionedController = deviceProvisionedController; mUserTracker = userTracker; + mQsPanelController = qsPanelController; mTunerService = tunerService; mMetricsLogger = metricsLogger; } @@ -262,7 +274,7 @@ public class QSFooterImplController extends ViewController impleme QSFooterImplController create(QSFooterImpl view) { QSFooterImplController controller = new QSFooterImplController(view, mUserManager, mUserInfoController, mActivityStarter, mDeviceProvisionedController, - mUserTracker, mTunerService, mMetricsLogger); + mUserTracker, mQsPanelController, mTunerService, mMetricsLogger); controller.init(); return controller; } 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/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/tests/src/com/android/systemui/qs/QSFooterImplControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplControllerTest.java index 293f3c901b51d..21248477c1ece 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplControllerTest.java @@ -72,6 +72,8 @@ public class QSFooterImplControllerTest extends LeakCheckedTest { @Mock private UserTracker mUserTracker; @Mock + private QSPanelController mQSPanelController; + @Mock private ClipboardManager mClipboardManager; private FakeTunerService mFakeTunerService; private MetricsLogger mMetricsLogger = new FakeMetricsLogger(); @@ -80,6 +82,8 @@ public class QSFooterImplControllerTest extends LeakCheckedTest { private SettingsButton mSettingsButton; @Mock private TextView mBuildText; + @Mock + private View mEdit; private QSFooterImplController mController; @@ -100,10 +104,11 @@ public class QSFooterImplControllerTest extends LeakCheckedTest { 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 QSFooterImplController(mView, mUserManager, mUserInfoController, - mActivityStarter, mDeviceProvisionedController, mUserTracker, mFakeTunerService, - mMetricsLogger); + mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController, + mFakeTunerService, mMetricsLogger); mController.init(); } From 7289d418766eb5bb7d7532d1cdd5fe81d3921c5a Mon Sep 17 00:00:00 2001 From: Dave Mankoff Date: Wed, 14 Oct 2020 16:51:42 -0400 Subject: [PATCH 5/5] 5/N Rename QSFooterImpl to QSFooterView Remove QSFooterImplController.Factory Bug: 168904199 Test: atest SystemUITests && manual Change-Id: Ie2ef5c620835c7ebb104316c0004e888af1c34cf --- .../SystemUI/res/layout/qs_footer_impl.xml | 4 +- .../{QSFooterImpl.java => QSFooterView.java} | 11 +++-- ...oller.java => QSFooterViewController.java} | 47 ++----------------- .../com/android/systemui/qs/QSFragment.java | 3 +- .../qs/dagger/QSFragmentComponent.java | 6 +-- .../systemui/qs/dagger/QSFragmentModule.java | 17 +++++++ ...t.java => QSFooterViewControllerTest.java} | 8 ++-- 7 files changed, 37 insertions(+), 59 deletions(-) rename packages/SystemUI/src/com/android/systemui/qs/{QSFooterImpl.java => QSFooterView.java} (97%) rename packages/SystemUI/src/com/android/systemui/qs/{QSFooterImplController.java => QSFooterViewController.java} (80%) rename packages/SystemUI/tests/src/com/android/systemui/qs/{QSFooterImplControllerTest.java => QSFooterViewControllerTest.java} (96%) 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/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java similarity index 97% rename from packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java rename to packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java index 399321261340e..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,7 +11,7 @@ * 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; @@ -51,7 +51,7 @@ import com.android.systemui.statusbar.phone.MultiUserSwitch; import com.android.systemui.statusbar.phone.SettingsButton; /** */ -public class QSFooterImpl extends FrameLayout { +public class QSFooterView extends FrameLayout { private SettingsButton mSettingsButton; protected View mSettingsContainer; private PageIndicator mPageIndicator; @@ -88,7 +88,7 @@ public class QSFooterImpl extends FrameLayout { } }; - public QSFooterImpl(Context context, AttributeSet attrs) { + public QSFooterView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -201,6 +201,7 @@ public class QSFooterImpl extends FrameLayout { updateEverything(isTunerEnabled); } + /** */ public void setExpansion(float headerExpansionFraction) { mExpansionAmount = headerExpansionFraction; if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction); @@ -225,6 +226,7 @@ public class QSFooterImpl extends FrameLayout { super.onDetachedFromWindow(); } + /** */ public void setListening(boolean listening) { if (listening == mListening) { return; @@ -287,6 +289,7 @@ public class QSFooterImpl extends FrameLayout { return mExpanded && mMultiUserSwitch.isMultiUserEnabled(); } + /** */ public void setQSPanel(final QSPanel qsPanel) { if (qsPanel != null) { mMultiUserSwitch.setQsPanel(qsPanel); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java similarity index 80% rename from packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java rename to packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java index ddd66b9642cda..e3af04bdc31e4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImplController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java @@ -44,10 +44,10 @@ import com.android.systemui.util.ViewController; import javax.inject.Inject; /** - * Controller for {@link QSFooterImpl}. + * Controller for {@link QSFooterView}. */ @QSScope -public class QSFooterImplController extends ViewController implements QSFooter { +public class QSFooterViewController extends ViewController implements QSFooter { private final UserManager mUserManager; private final UserInfoController mUserInfoController; @@ -116,7 +116,7 @@ public class QSFooterImplController extends ViewController impleme private boolean mExpanded; @Inject - QSFooterImplController(QSFooterImpl view, UserManager userManager, + QSFooterViewController(QSFooterView view, UserManager userManager, UserInfoController userInfoController, ActivityStarter activityStarter, DeviceProvisionedController deviceProvisionedController, UserTracker userTracker, QSPanelController qsPanelController, TunerService tunerService, @@ -238,45 +238,4 @@ public class QSFooterImplController extends ViewController impleme private boolean isTunerEnabled() { return mTunerService.isTunerEnabled(mUserTracker.getUserHandle()); } - - /** - * Factory for {@link QSFooterImplController}. - * - * TODO(b/168904199): Delete this once QSFooterImpl is no longer marked as injectable. - */ - @QSScope - public static class Factory { - 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; - - @Inject - Factory(UserManager userManager, UserInfoController userInfoController, - ActivityStarter activityStarter, - DeviceProvisionedController deviceProvisionedController, UserTracker userTracker, - QSPanelController qsPanelController, TunerService tunerService, - MetricsLogger metricsLogger) { - mUserManager = userManager; - mUserInfoController = userInfoController; - mActivityStarter = activityStarter; - mDeviceProvisionedController = deviceProvisionedController; - mUserTracker = userTracker; - mQsPanelController = qsPanelController; - mTunerService = tunerService; - mMetricsLogger = metricsLogger; - } - - QSFooterImplController create(QSFooterImpl view) { - QSFooterImplController controller = new QSFooterImplController(view, mUserManager, - mUserInfoController, mActivityStarter, mDeviceProvisionedController, - mUserTracker, mQsPanelController, mTunerService, mMetricsLogger); - controller.init(); - return controller; - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index ca91adba33640..1a7d366d84b40 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -142,8 +142,7 @@ 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 = qsFragmentComponent.getQSFooterFactory().create( - view.findViewById(R.id.qs_footer)); + mFooter = qsFragmentComponent.getQSFooter(); mQSContainerImplController = qsFragmentComponent.getQSContainerImplController(); mQSContainerImplController.init(); 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 59b9be62da129..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,7 +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.QSFooterImplController; +import com.android.systemui.qs.QSFooter; import com.android.systemui.qs.QSFragment; import com.android.systemui.qs.QSPanelController; import com.android.systemui.qs.QuickQSPanelController; @@ -50,6 +50,6 @@ public interface QSFragmentComponent { /** Construct a {@link QSContainerImplController}. */ QSContainerImplController getQSContainerImplController(); - /** */ - QSFooterImplController.Factory getQSFooterFactory(); + /** 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/tests/src/com/android/systemui/qs/QSFooterImplControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java similarity index 96% rename from packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplControllerTest.java rename to packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java index 21248477c1ece..065f236d19b32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java @@ -57,10 +57,10 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest -public class QSFooterImplControllerTest extends LeakCheckedTest { +public class QSFooterViewControllerTest extends LeakCheckedTest { @Mock - private QSFooterImpl mView; + private QSFooterView mView; @Mock private UserManager mUserManager; @Mock @@ -85,7 +85,7 @@ public class QSFooterImplControllerTest extends LeakCheckedTest { @Mock private View mEdit; - private QSFooterImplController mController; + private QSFooterViewController mController; @Before public void setup() throws Exception { @@ -106,7 +106,7 @@ public class QSFooterImplControllerTest extends LeakCheckedTest { when(mView.findViewById(R.id.build)).thenReturn(mBuildText); when(mView.findViewById(android.R.id.edit)).thenReturn(mEdit); - mController = new QSFooterImplController(mView, mUserManager, mUserInfoController, + mController = new QSFooterViewController(mView, mUserManager, mUserInfoController, mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController, mFakeTunerService, mMetricsLogger);