1/N Add QSFragmentComponent

This introduces the QSFragmentComponent as a way to create
QS related view controllers. The QSPanelController is introduced, but
doesn't do much other than proxy through most QSPanel calls.

Bug: 168904199
Test: manual && atest SystemUITests
Change-Id: I1fccda95f38722bf0215b2f15f65cd70ec66283a
This commit is contained in:
Dave Mankoff
2020-10-02 11:32:36 -04:00
parent 96aaf608b1
commit c078bc2ad0
11 changed files with 278 additions and 28 deletions

View File

@@ -19,7 +19,7 @@ package com.android.keyguard;
import android.view.ViewGroup;
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.keyguard.dagger.RootView;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.util.ViewController;

View File

@@ -24,6 +24,7 @@ import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardSecurityContainer;
import com.android.keyguard.KeyguardSecurityViewFlipper;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import dagger.Module;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.keyguard.dagger;
package com.android.systemui.dagger.qualifiers;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

View File

@@ -40,6 +40,7 @@ import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.qs.dagger.QSFragmentComponent;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -70,7 +71,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private HeightListener mPanelView;
protected QuickStatusBarHeader mHeader;
private QSCustomizer mQSCustomizer;
protected QSPanel mQSPanel;
protected NonInterceptingScrollView mQSPanelScrollView;
private QSDetail mQSDetail;
private boolean mListening;
@@ -82,6 +82,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
private final InjectionInflationController mInjectionInflater;
private final QSFragmentComponent.Factory mQsComponentFactory;
private final QSContainerImplController.Builder mQSContainerImplControllerBuilder;
private final QSTileHost mHost;
private boolean mShowCollapsedOnKeyguard;
@@ -96,14 +97,17 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private int[] mTmpLocation = new int[2];
private int mLastViewHeight;
private float mLastHeaderTranslation;
private QSPanelController mQSPanelController;
@Inject
public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
InjectionInflationController injectionInflater, QSTileHost qsTileHost,
StatusBarStateController statusBarStateController, CommandQueue commandQueue,
QSFragmentComponent.Factory qsComponentFactory,
QSContainerImplController.Builder qsContainerImplControllerBuilder) {
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
mInjectionInflater = injectionInflater;
mQsComponentFactory = qsComponentFactory;
mQSContainerImplControllerBuilder = qsContainerImplControllerBuilder;
commandQueue.observe(getLifecycle(), this);
mHost = qsTileHost;
@@ -120,8 +124,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mQSPanel = view.findViewById(R.id.quick_settings_panel);
QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
mQSPanelController = qsFragmentComponent.getQSPanelController();
mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
mQSPanelScrollView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
@@ -135,7 +140,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
});
mQSDetail = view.findViewById(R.id.qs_detail);
mHeader = view.findViewById(R.id.header);
mQSPanel.setHeaderContainer(view.findViewById(R.id.header_text_container));
mQSPanelController.setHeaderContainer(view.findViewById(R.id.header_text_container));
mFooter = view.findViewById(R.id.qs_footer);
mContainer = view.findViewById(id.quick_settings_container);
@@ -144,8 +149,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
.build();
mQSContainerImplController.init();
mQSDetail.setQsPanel(mQSPanel, mHeader, (View) mFooter);
mQSAnimator = new QSAnimator(this, mHeader.findViewById(R.id.quick_qs_panel), mQSPanel);
mQSDetail.setQsPanel(mQSPanelController.getView(), mHeader, (View) mFooter);
mQSAnimator = new QSAnimator(
this, mHeader.findViewById(R.id.quick_qs_panel), mQSPanelController.getView());
mQSCustomizer = view.findViewById(R.id.qs_customize);
@@ -156,7 +162,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
setEditLocation(view);
mQSCustomizer.restoreInstanceState(savedInstanceState);
if (mQsExpanded) {
mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
mQSPanelController.getTileLayout().restoreInstanceState(savedInstanceState);
}
}
setHost(mHost);
@@ -188,7 +194,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
outState.putBoolean(EXTRA_LISTENING, mListening);
mQSCustomizer.saveInstanceState(outState);
if (mQsExpanded) {
mQSPanel.getTileLayout().saveInstanceState(outState);
mQSPanelController.getTileLayout().saveInstanceState(outState);
}
}
@@ -249,9 +255,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
public void setHost(QSTileHost qsh) {
mQSPanel.setHost(qsh, mQSCustomizer);
mHeader.setQSPanel(mQSPanel);
mFooter.setQSPanel(mQSPanel);
mQSPanelController.setHost(qsh, mQSCustomizer);
mHeader.setQSPanel(mQSPanelController.getView());
mFooter.setQSPanel(mQSPanelController.getView());
mQSDetail.setHost(qsh);
if (mQSAnimator != null) {
@@ -278,7 +284,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private void updateQsState() {
final boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling
|| mHeaderAnimating;
mQSPanel.setExpanded(mQsExpanded);
mQSPanelController.setExpanded(mQsExpanded);
mQSDetail.setExpanded(mQsExpanded);
boolean keyguardShowing = isKeyguardShowing();
mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating
@@ -294,7 +300,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
: View.INVISIBLE);
mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
|| (mQsExpanded && !mStackScrollerOverscrolling));
mQSPanel.setVisibility(!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
mQSPanelController.setVisibility(
!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
}
private boolean isKeyguardShowing() {
@@ -318,7 +325,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
public QSPanel getQsPanel() {
return mQSPanel;
return mQSPanelController.getView();
}
public QSCustomizer getCustomizer() {
@@ -327,7 +334,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
@Override
public boolean isShowingDetail() {
return mQSPanel.isShowingCustomize() || mQSDetail.isShowingDetail();
return mQSPanelController.isShowingCustomize() || mQSDetail.isShowingDetail();
}
@Override
@@ -339,7 +346,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
public void setExpanded(boolean expanded) {
if (DEBUG) Log.d(TAG, "setExpanded " + expanded);
mQsExpanded = expanded;
mQSPanel.setListening(mListening, mQsExpanded);
mQSPanelController.setListening(mListening, mQsExpanded);
updateQsState();
}
@@ -368,7 +375,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mListening = listening;
mQSContainerImplController.setListening(listening);
mFooter.setListening(listening);
mQSPanel.setListening(mListening, mQsExpanded);
mQSPanelController.setListening(mListening, mQsExpanded);
}
@Override
@@ -409,8 +416,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mHeader.setExpansion(onKeyguardAndExpanded, expansion,
panelTranslationY);
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSPanel.getQsTileRevealController().setExpansion(expansion);
mQSPanel.getTileLayout().setExpansion(expansion);
mQSPanelController.getQsTileRevealController().setExpansion(expansion);
mQSPanelController.getTileLayout().setExpansion(expansion);
mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
if (fullyCollapsed) {
mQSPanelScrollView.setScrollY(0);
@@ -448,7 +455,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY()
+ mQSPanelScrollView.getScrollRange();
// The expanded media host should never move below the laid out position
pinToBottom(expandedMediaPosition, mQSPanel.getMediaHost(), true /* expanded */);
pinToBottom(
expandedMediaPosition, mQSPanelController.getMediaHost(), true /* expanded */);
// The expanded media host should never move above the laid out position
pinToBottom(absoluteBottomPosition, mHeader.getHeaderQsPanel().getMediaHost(),
false /* expanded */);
@@ -538,7 +546,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
@Override
public void closeDetail() {
mQSPanel.closeDetail();
mQSPanelController.closeDetail();
}
public void notifyCustomizeChanged() {

View File

@@ -0,0 +1,109 @@
/*
* 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.annotation.NonNull;
import android.view.ViewGroup;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.media.MediaHost;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
/**
* Controller for {@link QSPanel}.
*/
@QSScope
public class QSPanelController extends ViewController<QSPanel> {
private final BroadcastDispatcher mBroadcastDispatcher;
@Inject
QSPanelController(QSPanel view, BroadcastDispatcher broadcastDispatcher) {
super(view);
mBroadcastDispatcher = broadcastDispatcher;
}
@Override
protected void onViewAttached() {
}
@Override
protected void onViewDetached() {
}
/** TODO(b/168904199): Remove this method once view is controllerized. */
QSPanel getView() {
return mView;
}
/**
* Set the header container of quick settings.
*/
public void setHeaderContainer(@NonNull ViewGroup headerContainer) {
mView.setHeaderContainer(headerContainer);
}
public QSPanel.QSTileLayout getTileLayout() {
return mView.getTileLayout();
}
/** */
public void setHost(QSTileHost host, QSCustomizer customizer) {
mView.setHost(host, customizer);
}
/** */
public void setExpanded(boolean qsExpanded) {
mView.setExpanded(qsExpanded);
}
/** */
public boolean isShowingCustomize() {
return mView.isShowingCustomize();
}
/** */
public void setVisibility(int visibility) {
mView.setVisibility(visibility);
}
/** */
public void setListening(boolean listening, boolean expanded) {
mView.setListening(listening, expanded);
}
/** */
public QSTileRevealController getQsTileRevealController() {
return mView.getQsTileRevealController();
}
/** */
public MediaHost getMediaHost() {
return mView.getMediaHost();
}
/** */
public void closeDetail() {
mView.closeDetail();
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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.dagger;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import dagger.BindsInstance;
import dagger.Subcomponent;
/**
* Dagger Subcomponent for {@link QSFragment}.
*/
@Subcomponent(modules = {QSFragmentModule.class})
@QSScope
public interface QSFragmentComponent {
/** Factory for building a {@link QSFragmentComponent}. */
@Subcomponent.Factory
interface Factory {
QSFragmentComponent create(@BindsInstance QSFragment qsFragment);
}
/** Construct a {@link QSPanelController}. */
QSPanelController getQSPanelController();
}

View File

@@ -0,0 +1,46 @@
/*
* 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.dagger;
import android.view.View;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
import dagger.Module;
import dagger.Provides;
/**
* Dagger Module for {@link QSFragmentComponent}.
*/
@Module
public interface QSFragmentModule {
/** */
@Provides
@RootView
static View provideRootView(QSFragment qsFragment) {
return qsFragment.getView();
}
/** */
@Provides
static QSPanel provideQSPanel(@RootView View view) {
return view.findViewById(R.id.quick_settings_panel);
}
}

View File

@@ -38,8 +38,8 @@ import dagger.Provides;
/**
* Module for QS dependencies
*/
// TODO: Add other QS classes
@Module(includes = {MediaModule.class})
@Module(subcomponents = {QSFragmentComponent.class},
includes = {MediaModule.class})
public interface QSModule {
@Provides
@@ -60,7 +60,6 @@ public interface QSModule {
return manager;
}
/** */
@Binds
QSHost provideQsHost(QSTileHost controllerImpl);

View File

@@ -0,0 +1,32 @@
/*
* 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.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import javax.inject.Scope;
/**
* Scope annotation for singleton items within the {@link QSFragmentComponent}.
*/
@Documented
@Retention(RUNTIME)
@Scope
public @interface QSScope {}

View File

@@ -38,9 +38,9 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.keyguard.dagger.RootView;
import com.android.systemui.DejankUtils;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.dagger.qualifiers.RootView;
import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;

View File

@@ -16,7 +16,9 @@ package com.android.systemui.qs;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.Fragment;
import android.content.Context;
@@ -42,6 +44,7 @@ import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.settings.UserTracker;
@@ -61,6 +64,8 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Optional;
@@ -71,6 +76,12 @@ import java.util.Optional;
public class QSFragmentTest extends SysuiBaseFragmentTest {
private MetricsLogger mMockMetricsLogger;
@Mock
private QSFragmentComponent.Factory mQsComponentFactory;
@Mock
private QSFragmentComponent mQsFragmentComponent;
@Mock
private QSPanelController mQSPanelController;
public QSFragmentTest() {
super(QSFragment.class);
@@ -80,6 +91,10 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Before
@Ignore("failing")
public void addLeakCheckDependencies() {
MockitoAnnotations.initMocks(this);
when(mQsComponentFactory.create(any(View.class))).thenReturn(mQsFragmentComponent);
when(mQsFragmentComponent.getQSPanelController()).thenReturn(mQSPanelController);
mMockMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
new LayoutInflaterBuilder(mContext)
@@ -152,6 +167,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
mock(QSTileHost.class),
mock(StatusBarStateController.class),
commandQueue,
mQsComponentFactory,
mock(QSContainerImplController.Builder.class));
}
}