1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.qs;
18 
19 import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
20 import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
21 import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
22 import static com.android.systemui.qs.dagger.QSScopeModule.QS_USING_MEDIA_PLAYER;
23 
24 import android.view.MotionEvent;
25 import android.view.View;
26 
27 import androidx.annotation.Nullable;
28 
29 import com.android.internal.logging.MetricsLogger;
30 import com.android.internal.logging.UiEventLogger;
31 import com.android.systemui.dump.DumpManager;
32 import com.android.systemui.haptics.qs.QSLongPressEffect;
33 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor;
34 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
35 import com.android.systemui.media.controls.ui.view.MediaHost;
36 import com.android.systemui.media.controls.ui.view.MediaHostState;
37 import com.android.systemui.plugins.FalsingManager;
38 import com.android.systemui.qs.customize.QSCustomizerController;
39 import com.android.systemui.qs.dagger.QSScope;
40 import com.android.systemui.qs.logging.QSLogger;
41 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
42 import com.android.systemui.settings.brightness.BrightnessController;
43 import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
44 import com.android.systemui.settings.brightness.BrightnessSliderController;
45 import com.android.systemui.settings.brightness.MirrorController;
46 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
47 import com.android.systemui.statusbar.policy.SplitShadeStateController;
48 import com.android.systemui.tuner.TunerService;
49 
50 import kotlinx.coroutines.flow.StateFlow;
51 
52 import javax.inject.Inject;
53 import javax.inject.Named;
54 import javax.inject.Provider;
55 
56 
57 /**
58  * Controller for {@link QSPanel}.
59  */
60 @QSScope
61 public class QSPanelController extends QSPanelControllerBase<QSPanel> {
62 
63     private final TunerService mTunerService;
64     private final QSCustomizerController mQsCustomizerController;
65     private final QSTileRevealController.Factory mQsTileRevealControllerFactory;
66     private final FalsingManager mFalsingManager;
67     private BrightnessController mBrightnessController;
68     private BrightnessSliderController mBrightnessSliderController;
69     private BrightnessMirrorHandler mBrightnessMirrorHandler;
70     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
71     private boolean mListening;
72 
73     private final boolean mSceneContainerEnabled;
74 
75     private int mLastDensity;
76     private final BrightnessSliderController.Factory mBrightnessSliderControllerFactory;
77     private final BrightnessController.Factory mBrightnessControllerFactory;
78 
79     protected final MediaCarouselInteractor mMediaCarouselInteractor;
80 
81     private View.OnTouchListener mTileLayoutTouchListener = new View.OnTouchListener() {
82         @Override
83         public boolean onTouch(View v, MotionEvent event) {
84             if (event.getActionMasked() == MotionEvent.ACTION_UP) {
85                 mFalsingManager.isFalseTouch(QS_SWIPE_SIDE);
86             }
87             return false;
88         }
89     };
90 
91     @Inject
QSPanelController(QSPanel view, TunerService tunerService, QSHost qsHost, QSCustomizerController qsCustomizerController, @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer, @Named(QS_PANEL) MediaHost mediaHost, QSTileRevealController.Factory qsTileRevealControllerFactory, DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory, BrightnessSliderController.Factory brightnessSliderFactory, FalsingManager falsingManager, StatusBarKeyguardViewManager statusBarKeyguardViewManager, SplitShadeStateController splitShadeStateController, Provider<QSLongPressEffect> longPRessEffectProvider, MediaCarouselInteractor mediaCarouselInteractor)92     QSPanelController(QSPanel view, TunerService tunerService,
93             QSHost qsHost, QSCustomizerController qsCustomizerController,
94             @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
95             @Named(QS_PANEL) MediaHost mediaHost,
96             QSTileRevealController.Factory qsTileRevealControllerFactory,
97             DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
98             QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
99             BrightnessSliderController.Factory brightnessSliderFactory,
100             FalsingManager falsingManager,
101             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
102             SplitShadeStateController splitShadeStateController,
103             Provider<QSLongPressEffect> longPRessEffectProvider,
104             MediaCarouselInteractor mediaCarouselInteractor) {
105         super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost,
106                 metricsLogger, uiEventLogger, qsLogger, dumpManager, splitShadeStateController,
107                 longPRessEffectProvider);
108         mTunerService = tunerService;
109         mQsCustomizerController = qsCustomizerController;
110         mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
111         mFalsingManager = falsingManager;
112         mBrightnessSliderControllerFactory = brightnessSliderFactory;
113         mBrightnessControllerFactory = brightnessControllerFactory;
114 
115         mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView);
116         mView.setBrightnessView(mBrightnessSliderController.getRootView());
117 
118         mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
119         mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
120         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
121         mLastDensity = view.getResources().getConfiguration().densityDpi;
122         mSceneContainerEnabled = SceneContainerFlag.isEnabled();
123         mMediaCarouselInteractor = mediaCarouselInteractor;
124     }
125 
126     @Override
onInit()127     public void onInit() {
128         super.onInit();
129         mMediaHost.setExpansion(MediaHostState.EXPANDED);
130         mMediaHost.setShowsOnlyActiveMedia(false);
131         mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
132         mQsCustomizerController.init();
133         mBrightnessSliderController.init();
134     }
135 
136     @Override
getMediaVisibleFlow()137     StateFlow<Boolean> getMediaVisibleFlow() {
138         return mMediaCarouselInteractor.getHasAnyMediaOrRecommendation();
139     }
140 
141     @Override
onViewAttached()142     protected void onViewAttached() {
143         super.onViewAttached();
144 
145         updateMediaDisappearParameters();
146 
147         mTunerService.addTunable(mView, QS_SHOW_BRIGHTNESS);
148         mView.updateResources();
149         mView.setSceneContainerEnabled(mSceneContainerEnabled);
150         if (mView.isListening()) {
151             refreshAllTiles();
152         }
153         switchTileLayout(true);
154         mBrightnessMirrorHandler.onQsPanelAttached();
155         PagedTileLayout pagedTileLayout= ((PagedTileLayout) mView.getOrCreateTileLayout());
156         pagedTileLayout.setOnTouchListener(mTileLayoutTouchListener);
157         maybeReinflateBrightnessSlider();
158     }
159 
160     @Override
createTileRevealController()161     protected QSTileRevealController createTileRevealController() {
162         return mQsTileRevealControllerFactory.create(
163                 this, (PagedTileLayout) mView.getOrCreateTileLayout());
164     }
165 
166     @Override
onViewDetached()167     protected void onViewDetached() {
168         mTunerService.removeTunable(mView);
169         mBrightnessMirrorHandler.onQsPanelDettached();
170         super.onViewDetached();
171     }
172 
173     @Override
onConfigurationChanged()174     protected void onConfigurationChanged() {
175         mView.updateResources();
176         maybeReinflateBrightnessSlider();
177         if (mView.isListening()) {
178             refreshAllTiles();
179         }
180     }
181 
maybeReinflateBrightnessSlider()182     private void maybeReinflateBrightnessSlider() {
183         int newDensity = mView.getResources().getConfiguration().densityDpi;
184         if (newDensity != mLastDensity) {
185             mLastDensity = newDensity;
186             reinflateBrightnessSlider();
187         }
188     }
189 
reinflateBrightnessSlider()190     private void reinflateBrightnessSlider() {
191         mBrightnessController.unregisterCallbacks();
192         mBrightnessSliderController =
193                 mBrightnessSliderControllerFactory.create(getContext(), mView);
194         mView.setBrightnessView(mBrightnessSliderController.getRootView());
195         mBrightnessController = mBrightnessControllerFactory.create(mBrightnessSliderController);
196         mBrightnessMirrorHandler.setBrightnessController(mBrightnessController);
197         mBrightnessSliderController.init();
198         if (mListening) {
199             mBrightnessController.registerCallbacks();
200         }
201     }
202 
203 
204     @Override
onSplitShadeChanged(boolean shouldUseSplitNotificationShade)205     protected void onSplitShadeChanged(boolean shouldUseSplitNotificationShade) {
206         ((PagedTileLayout) mView.getOrCreateTileLayout())
207                 .forceTilesRedistribution("Split shade state changed");
208         mView.setCanCollapse(!shouldUseSplitNotificationShade);
209     }
210 
211     /** */
setVisibility(int visibility)212     public void setVisibility(int visibility) {
213         mView.setVisibility(visibility);
214     }
215 
216     /** */
setListening(boolean listening, boolean expanded)217     public void setListening(boolean listening, boolean expanded) {
218         setListening(listening && expanded);
219 
220         if (listening != mListening) {
221             mListening = listening;
222             // Set the listening as soon as the QS fragment starts listening regardless of the
223             //expansion, so it will update the current brightness before the slider is visible.
224             if (listening) {
225                 mBrightnessController.registerCallbacks();
226             } else {
227                 mBrightnessController.unregisterCallbacks();
228             }
229         }
230     }
231 
setBrightnessMirror(@ullable MirrorController brightnessMirrorController)232     public void setBrightnessMirror(@Nullable MirrorController brightnessMirrorController) {
233         mBrightnessMirrorHandler.setController(brightnessMirrorController);
234     }
235 
236     /** Update appearance of QSPanel. */
updateResources()237     public void updateResources() {
238         mView.updateResources();
239     }
240 
241     /** Update state of all tiles. */
refreshAllTiles()242     public void refreshAllTiles() {
243         mBrightnessController.checkRestrictionAndSetEnabled();
244         super.refreshAllTiles();
245     }
246 
247     /** Start customizing the Quick Settings. */
showEdit(View view)248     public void showEdit(View view) {
249         view.post(() -> {
250             if (!mQsCustomizerController.isCustomizing()) {
251                 int[] loc = view.getLocationOnScreen();
252                 int x = loc[0] + view.getWidth() / 2;
253                 int y = loc[1] + view.getHeight() / 2;
254                 mQsCustomizerController.show(x, y, false);
255             }
256         });
257     }
258 
isLayoutRtl()259     public boolean isLayoutRtl() {
260         return mView.isLayoutRtl();
261     }
262 
263     /** */
setPageListener(PagedTileLayout.PageListener listener)264     public void setPageListener(PagedTileLayout.PageListener listener) {
265         mView.setPageListener(listener);
266     }
267 
isShown()268     public boolean isShown() {
269         return mView.isShown();
270     }
271 
272     /** */
setContentMargins(int startMargin, int endMargin)273     public void setContentMargins(int startMargin, int endMargin) {
274         mView.setContentMargins(startMargin, endMargin, mMediaHost.getHostView());
275     }
276 
277     /** */
setFooterPageIndicator(PageIndicator pageIndicator)278     public void setFooterPageIndicator(PageIndicator pageIndicator) {
279         mView.setFooterPageIndicator(pageIndicator);
280     }
281 
282     /** */
isExpanded()283     public boolean isExpanded() {
284         return mView.isExpanded();
285     }
286 
setPageMargin(int pageMargin)287     void setPageMargin(int pageMargin) {
288         mView.setPageMargin(pageMargin);
289     }
290 
291     /**
292      * Determines if bouncer expansion is between 0 and 1 non-inclusive.
293      *
294      * @return if bouncer is in transit
295      */
isBouncerInTransit()296     public boolean isBouncerInTransit() {
297         return mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit();
298     }
299 
getPaddingBottom()300     public int getPaddingBottom() {
301         return mView.getPaddingBottom();
302     }
303 
getViewBottom()304     int getViewBottom() {
305         return mView.getBottom();
306     }
307 }
308 
309