Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file |
| 5 | * except in compliance with the License. You may obtain a copy of the License at |
| 6 | * |
| 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | * |
| 9 | * Unless required by applicable law or agreed to in writing, software distributed under the |
| 10 | * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 11 | * KIND, either express or implied. See the License for the specific language governing |
| 12 | * permissions and limitations under the License. |
| 13 | */ |
| 14 | |
| 15 | package com.android.systemui.qs; |
| 16 | |
Amin Shaikh | f09450b | 2018-04-06 17:32:45 -0400 | [diff] [blame] | 17 | import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; |
| 18 | |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 19 | import android.animation.Animator; |
| 20 | import android.animation.AnimatorListenerAdapter; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 21 | import android.content.res.Configuration; |
| 22 | import android.graphics.Rect; |
| 23 | import android.os.Bundle; |
| 24 | import android.util.Log; |
Jason Monk | 9a376bc | 2017-05-10 09:52:10 -0400 | [diff] [blame] | 25 | import android.view.ContextThemeWrapper; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 26 | import android.view.LayoutInflater; |
Jason Monk | 231b052 | 2018-01-04 10:49:55 -0500 | [diff] [blame] | 27 | import android.view.MotionEvent; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 28 | import android.view.View; |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 29 | import android.view.View.OnClickListener; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 30 | import android.view.ViewGroup; |
| 31 | import android.view.ViewTreeObserver; |
| 32 | import android.widget.FrameLayout.LayoutParams; |
| 33 | |
Gus Prevas | ab33679 | 2018-11-14 13:52:20 -0500 | [diff] [blame] | 34 | import androidx.annotation.Nullable; |
| 35 | import androidx.annotation.VisibleForTesting; |
| 36 | |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 37 | import com.android.systemui.Interpolators; |
| 38 | import com.android.systemui.R; |
Jason Monk | b4cc7b1 | 2017-05-09 13:50:47 -0400 | [diff] [blame] | 39 | import com.android.systemui.R.id; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 40 | import com.android.systemui.plugins.qs.QS; |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 41 | import com.android.systemui.plugins.statusbar.StatusBarStateController; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 42 | import com.android.systemui.qs.customize.QSCustomizer; |
Amin Shaikh | f09450b | 2018-04-06 17:32:45 -0400 | [diff] [blame] | 43 | import com.android.systemui.statusbar.CommandQueue; |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 44 | import com.android.systemui.statusbar.StatusBarState; |
Gus Prevas | ab33679 | 2018-11-14 13:52:20 -0500 | [diff] [blame] | 45 | import com.android.systemui.statusbar.notification.stack.StackStateAnimator; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 46 | import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; |
sanryhuang | 6378786 | 2018-05-18 15:57:43 +0800 | [diff] [blame] | 47 | import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; |
Jason Monk | ea54e8a | 2018-12-20 10:01:48 -0500 | [diff] [blame] | 48 | import com.android.systemui.util.InjectionInflationController; |
Jason Monk | 8966d99 | 2018-12-20 14:16:29 -0500 | [diff] [blame] | 49 | import com.android.systemui.util.LifecycleFragment; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 50 | |
Jason Monk | 48f85be | 2018-12-19 15:16:44 -0500 | [diff] [blame] | 51 | import javax.inject.Inject; |
| 52 | |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 53 | public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Callbacks, |
| 54 | StatusBarStateController.StateListener { |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 55 | private static final String TAG = "QS"; |
| 56 | private static final boolean DEBUG = false; |
Jason Monk | 64b214e | 2017-03-27 13:40:59 -0400 | [diff] [blame] | 57 | private static final String EXTRA_EXPANDED = "expanded"; |
| 58 | private static final String EXTRA_LISTENING = "listening"; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 59 | |
| 60 | private final Rect mQsBounds = new Rect(); |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 61 | private final StatusBarStateController mStatusBarStateController; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 62 | private boolean mQsExpanded; |
| 63 | private boolean mHeaderAnimating; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 64 | private boolean mStackScrollerOverscrolling; |
| 65 | |
| 66 | private long mDelay; |
| 67 | |
| 68 | private QSAnimator mQSAnimator; |
| 69 | private HeightListener mPanelView; |
| 70 | protected QuickStatusBarHeader mHeader; |
| 71 | private QSCustomizer mQSCustomizer; |
| 72 | protected QSPanel mQSPanel; |
| 73 | private QSDetail mQSDetail; |
| 74 | private boolean mListening; |
| 75 | private QSContainerImpl mContainer; |
| 76 | private int mLayoutDirection; |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 77 | private QSFooter mFooter; |
Selim Cinek | 1a891a9 | 2017-12-04 17:41:27 +0100 | [diff] [blame] | 78 | private float mLastQSExpansion = -1; |
Amin Shaikh | f09450b | 2018-04-06 17:32:45 -0400 | [diff] [blame] | 79 | private boolean mQsDisabled; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 80 | |
Jason Monk | 48f85be | 2018-12-19 15:16:44 -0500 | [diff] [blame] | 81 | private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; |
Jason Monk | ea54e8a | 2018-12-20 10:01:48 -0500 | [diff] [blame] | 82 | private final InjectionInflationController mInjectionInflater; |
Dave Mankoff | c4fe6c4 | 2019-11-18 17:03:29 -0500 | [diff] [blame] | 83 | private final QSContainerImplController.Builder mQSContainerImplControllerBuilder; |
Jason Monk | 5d57720 | 2018-12-26 15:43:06 -0500 | [diff] [blame] | 84 | private final QSTileHost mHost; |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 85 | private boolean mShowCollapsedOnKeyguard; |
| 86 | private boolean mLastKeyguardAndExpanded; |
| 87 | /** |
| 88 | * The last received state from the controller. This should not be used directly to check if |
| 89 | * we're on keyguard but use {@link #isKeyguardShowing()} instead since that is more accurate |
| 90 | * during state transitions which often call into us. |
| 91 | */ |
| 92 | private int mState; |
Dave Mankoff | c4fe6c4 | 2019-11-18 17:03:29 -0500 | [diff] [blame] | 93 | private QSContainerImplController mQSContainerImplController; |
Jason Monk | 48f85be | 2018-12-19 15:16:44 -0500 | [diff] [blame] | 94 | |
| 95 | @Inject |
Jason Monk | ea54e8a | 2018-12-20 10:01:48 -0500 | [diff] [blame] | 96 | public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler, |
Dave Mankoff | c4fe6c4 | 2019-11-18 17:03:29 -0500 | [diff] [blame] | 97 | InjectionInflationController injectionInflater, QSTileHost qsTileHost, |
| 98 | StatusBarStateController statusBarStateController, CommandQueue commandQueue, |
| 99 | QSContainerImplController.Builder qsContainerImplControllerBuilder) { |
Jason Monk | 48f85be | 2018-12-19 15:16:44 -0500 | [diff] [blame] | 100 | mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler; |
Jason Monk | ea54e8a | 2018-12-20 10:01:48 -0500 | [diff] [blame] | 101 | mInjectionInflater = injectionInflater; |
Dave Mankoff | c4fe6c4 | 2019-11-18 17:03:29 -0500 | [diff] [blame] | 102 | mQSContainerImplControllerBuilder = qsContainerImplControllerBuilder; |
Dave Mankoff | bcaca8a | 2019-10-31 18:04:08 -0400 | [diff] [blame] | 103 | commandQueue.observe(getLifecycle(), this); |
Jason Monk | 5d57720 | 2018-12-26 15:43:06 -0500 | [diff] [blame] | 104 | mHost = qsTileHost; |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 105 | mStatusBarStateController = statusBarStateController; |
Jason Monk | 48f85be | 2018-12-19 15:16:44 -0500 | [diff] [blame] | 106 | } |
sanryhuang | 6378786 | 2018-05-18 15:57:43 +0800 | [diff] [blame] | 107 | |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 108 | @Override |
| 109 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, |
| 110 | Bundle savedInstanceState) { |
Jason Monk | ea54e8a | 2018-12-20 10:01:48 -0500 | [diff] [blame] | 111 | inflater = mInjectionInflater.injectable( |
| 112 | inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme))); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 113 | return inflater.inflate(R.layout.qs_panel, container, false); |
| 114 | } |
| 115 | |
| 116 | @Override |
| 117 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { |
| 118 | super.onViewCreated(view, savedInstanceState); |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 119 | mQSPanel = view.findViewById(R.id.quick_settings_panel); |
| 120 | mQSDetail = view.findViewById(R.id.qs_detail); |
| 121 | mHeader = view.findViewById(R.id.header); |
| 122 | mFooter = view.findViewById(R.id.qs_footer); |
Jason Monk | b4cc7b1 | 2017-05-09 13:50:47 -0400 | [diff] [blame] | 123 | mContainer = view.findViewById(id.quick_settings_container); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 124 | |
Dave Mankoff | c4fe6c4 | 2019-11-18 17:03:29 -0500 | [diff] [blame] | 125 | mQSContainerImplController = mQSContainerImplControllerBuilder |
| 126 | .setQSContainerImpl((QSContainerImpl) view) |
| 127 | .build(); |
| 128 | |
| 129 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 130 | mQSDetail.setQsPanel(mQSPanel, mHeader, (View) mFooter); |
| 131 | mQSAnimator = new QSAnimator(this, |
| 132 | mHeader.findViewById(R.id.quick_qs_panel), mQSPanel); |
Anthony Chen | 3cb3ad9 | 2016-12-01 10:58:47 -0800 | [diff] [blame] | 133 | |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 134 | mQSCustomizer = view.findViewById(R.id.qs_customize); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 135 | mQSCustomizer.setQs(this); |
Jason Monk | 64b214e | 2017-03-27 13:40:59 -0400 | [diff] [blame] | 136 | if (savedInstanceState != null) { |
| 137 | setExpanded(savedInstanceState.getBoolean(EXTRA_EXPANDED)); |
| 138 | setListening(savedInstanceState.getBoolean(EXTRA_LISTENING)); |
Fabian Kozynski | 0db39af | 2018-08-31 15:56:45 -0400 | [diff] [blame] | 139 | setEditLocation(view); |
Selim Cinek | 707e207 | 2017-06-30 18:32:40 +0200 | [diff] [blame] | 140 | mQSCustomizer.restoreInstanceState(savedInstanceState); |
Fabian Kozynski | 407ddb2 | 2018-10-03 15:04:56 -0400 | [diff] [blame] | 141 | if (mQsExpanded) { |
| 142 | mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState); |
| 143 | } |
Jason Monk | 64b214e | 2017-03-27 13:40:59 -0400 | [diff] [blame] | 144 | } |
Jason Monk | 5d57720 | 2018-12-26 15:43:06 -0500 | [diff] [blame] | 145 | setHost(mHost); |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 146 | mStatusBarStateController.addCallback(this); |
| 147 | onStateChanged(mStatusBarStateController.getState()); |
Jason Monk | 64b214e | 2017-03-27 13:40:59 -0400 | [diff] [blame] | 148 | } |
| 149 | |
| 150 | @Override |
| 151 | public void onDestroy() { |
| 152 | super.onDestroy(); |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 153 | mStatusBarStateController.removeCallback(this); |
Jason Monk | 64b214e | 2017-03-27 13:40:59 -0400 | [diff] [blame] | 154 | if (mListening) { |
| 155 | setListening(false); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | @Override |
| 160 | public void onSaveInstanceState(Bundle outState) { |
| 161 | super.onSaveInstanceState(outState); |
| 162 | outState.putBoolean(EXTRA_EXPANDED, mQsExpanded); |
| 163 | outState.putBoolean(EXTRA_LISTENING, mListening); |
Selim Cinek | 707e207 | 2017-06-30 18:32:40 +0200 | [diff] [blame] | 164 | mQSCustomizer.saveInstanceState(outState); |
Fabian Kozynski | 407ddb2 | 2018-10-03 15:04:56 -0400 | [diff] [blame] | 165 | if (mQsExpanded) { |
| 166 | mQSPanel.getTileLayout().saveInstanceState(outState); |
| 167 | } |
Jason Monk | 64b214e | 2017-03-27 13:40:59 -0400 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | @VisibleForTesting |
| 171 | boolean isListening() { |
| 172 | return mListening; |
| 173 | } |
| 174 | |
| 175 | @VisibleForTesting |
| 176 | boolean isExpanded() { |
| 177 | return mQsExpanded; |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 178 | } |
| 179 | |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 180 | @Override |
| 181 | public View getHeader() { |
| 182 | return mHeader; |
| 183 | } |
| 184 | |
Jason Monk | 4de5d3c | 2017-05-24 14:57:10 -0400 | [diff] [blame] | 185 | @Override |
| 186 | public void setHasNotifications(boolean hasNotifications) { |
Jason Monk | 4de5d3c | 2017-05-24 14:57:10 -0400 | [diff] [blame] | 187 | } |
| 188 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 189 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 190 | public void setPanelView(HeightListener panelView) { |
| 191 | mPanelView = panelView; |
| 192 | } |
| 193 | |
| 194 | @Override |
| 195 | public void onConfigurationChanged(Configuration newConfig) { |
| 196 | super.onConfigurationChanged(newConfig); |
Fabian Kozynski | 0db39af | 2018-08-31 15:56:45 -0400 | [diff] [blame] | 197 | setEditLocation(getView()); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 198 | if (newConfig.getLayoutDirection() != mLayoutDirection) { |
| 199 | mLayoutDirection = newConfig.getLayoutDirection(); |
Anthony Chen | 3cb3ad9 | 2016-12-01 10:58:47 -0800 | [diff] [blame] | 200 | if (mQSAnimator != null) { |
| 201 | mQSAnimator.onRtlChanged(); |
| 202 | } |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 203 | } |
| 204 | } |
| 205 | |
Fabian Kozynski | 0db39af | 2018-08-31 15:56:45 -0400 | [diff] [blame] | 206 | private void setEditLocation(View view) { |
Fabian Kozynski | 0db39af | 2018-08-31 15:56:45 -0400 | [diff] [blame] | 207 | View edit = view.findViewById(android.R.id.edit); |
| 208 | int[] loc = edit.getLocationOnScreen(); |
| 209 | int x = loc[0] + edit.getWidth() / 2; |
| 210 | int y = loc[1] + edit.getHeight() / 2; |
| 211 | mQSCustomizer.setEditLocation(x, y); |
| 212 | } |
| 213 | |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 214 | @Override |
| 215 | public void setContainer(ViewGroup container) { |
| 216 | if (container instanceof NotificationsQuickSettingsContainer) { |
| 217 | mQSCustomizer.setContainer((NotificationsQuickSettingsContainer) container); |
| 218 | } |
| 219 | } |
| 220 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 221 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 222 | public boolean isCustomizing() { |
| 223 | return mQSCustomizer.isCustomizing(); |
| 224 | } |
| 225 | |
| 226 | public void setHost(QSTileHost qsh) { |
| 227 | mQSPanel.setHost(qsh, mQSCustomizer); |
| 228 | mHeader.setQSPanel(mQSPanel); |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 229 | mFooter.setQSPanel(mQSPanel); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 230 | mQSDetail.setHost(qsh); |
Anthony Chen | 3cb3ad9 | 2016-12-01 10:58:47 -0800 | [diff] [blame] | 231 | |
| 232 | if (mQSAnimator != null) { |
| 233 | mQSAnimator.setHost(qsh); |
| 234 | } |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 235 | } |
| 236 | |
Amin Shaikh | f09450b | 2018-04-06 17:32:45 -0400 | [diff] [blame] | 237 | @Override |
Charles Chen | f3d295c | 2018-11-30 18:15:21 +0800 | [diff] [blame] | 238 | public void disable(int displayId, int state1, int state2, boolean animate) { |
| 239 | if (displayId != getContext().getDisplayId()) { |
| 240 | return; |
| 241 | } |
sanryhuang | 6378786 | 2018-05-18 15:57:43 +0800 | [diff] [blame] | 242 | state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2); |
| 243 | |
Amin Shaikh | f09450b | 2018-04-06 17:32:45 -0400 | [diff] [blame] | 244 | final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0; |
| 245 | if (disabled == mQsDisabled) return; |
| 246 | mQsDisabled = disabled; |
| 247 | mContainer.disable(state1, state2, animate); |
| 248 | mHeader.disable(state1, state2, animate); |
| 249 | mFooter.disable(state1, state2, animate); |
| 250 | updateQsState(); |
| 251 | } |
| 252 | |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 253 | private void updateQsState() { |
| 254 | final boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling |
| 255 | || mHeaderAnimating; |
| 256 | mQSPanel.setExpanded(mQsExpanded); |
| 257 | mQSDetail.setExpanded(mQsExpanded); |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 258 | boolean keyguardShowing = isKeyguardShowing(); |
| 259 | mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating |
| 260 | || mShowCollapsedOnKeyguard) |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 261 | ? View.VISIBLE |
| 262 | : View.INVISIBLE); |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 263 | mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard) |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 264 | || (mQsExpanded && !mStackScrollerOverscrolling)); |
Amin Shaikh | e04505e | 2018-04-18 10:45:13 -0400 | [diff] [blame] | 265 | mFooter.setVisibility( |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 266 | !mQsDisabled && (mQsExpanded || !keyguardShowing || mHeaderAnimating |
| 267 | || mShowCollapsedOnKeyguard) |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 268 | ? View.VISIBLE |
| 269 | : View.INVISIBLE); |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 270 | mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard) |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 271 | || (mQsExpanded && !mStackScrollerOverscrolling)); |
Amin Shaikh | e04505e | 2018-04-18 10:45:13 -0400 | [diff] [blame] | 272 | mQSPanel.setVisibility(!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 273 | } |
| 274 | |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 275 | private boolean isKeyguardShowing() { |
| 276 | // We want the freshest state here since otherwise we'll have some weirdness if earlier |
| 277 | // listeners trigger updates |
| 278 | return mStatusBarStateController.getState() == StatusBarState.KEYGUARD; |
| 279 | } |
| 280 | |
| 281 | @Override |
| 282 | public void setShowCollapsedOnKeyguard(boolean showCollapsedOnKeyguard) { |
| 283 | if (showCollapsedOnKeyguard != mShowCollapsedOnKeyguard) { |
| 284 | mShowCollapsedOnKeyguard = showCollapsedOnKeyguard; |
| 285 | updateQsState(); |
| 286 | if (mQSAnimator != null) { |
| 287 | mQSAnimator.setShowCollapsedOnKeyguard(showCollapsedOnKeyguard); |
| 288 | } |
| 289 | if (!showCollapsedOnKeyguard && isKeyguardShowing()) { |
| 290 | setQsExpansion(mLastQSExpansion, 0); |
| 291 | } |
| 292 | } |
| 293 | } |
| 294 | |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 295 | public QSPanel getQsPanel() { |
| 296 | return mQSPanel; |
| 297 | } |
| 298 | |
| 299 | public QSCustomizer getCustomizer() { |
| 300 | return mQSCustomizer; |
| 301 | } |
| 302 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 303 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 304 | public boolean isShowingDetail() { |
| 305 | return mQSPanel.isShowingCustomize() || mQSDetail.isShowingDetail(); |
| 306 | } |
| 307 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 308 | @Override |
Jason Monk | 231b052 | 2018-01-04 10:49:55 -0500 | [diff] [blame] | 309 | public boolean onInterceptTouchEvent(MotionEvent event) { |
Amin Shaikh | 15781d6 | 2018-02-16 16:00:13 -0500 | [diff] [blame] | 310 | return isCustomizing(); |
Jason Monk | 231b052 | 2018-01-04 10:49:55 -0500 | [diff] [blame] | 311 | } |
| 312 | |
| 313 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 314 | public void setHeaderClickable(boolean clickable) { |
| 315 | if (DEBUG) Log.d(TAG, "setHeaderClickable " + clickable); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 316 | } |
| 317 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 318 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 319 | public void setExpanded(boolean expanded) { |
| 320 | if (DEBUG) Log.d(TAG, "setExpanded " + expanded); |
| 321 | mQsExpanded = expanded; |
Fabian Kozynski | b9dc0c7 | 2018-09-20 13:00:17 -0400 | [diff] [blame] | 322 | mQSPanel.setListening(mListening, mQsExpanded); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 323 | updateQsState(); |
| 324 | } |
| 325 | |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 326 | private void setKeyguardShowing(boolean keyguardShowing) { |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 327 | if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing); |
Selim Cinek | 1a891a9 | 2017-12-04 17:41:27 +0100 | [diff] [blame] | 328 | mLastQSExpansion = -1; |
Anthony Chen | 3cb3ad9 | 2016-12-01 10:58:47 -0800 | [diff] [blame] | 329 | |
| 330 | if (mQSAnimator != null) { |
| 331 | mQSAnimator.setOnKeyguard(keyguardShowing); |
| 332 | } |
| 333 | |
Jason Monk | 9dac112 | 2017-05-30 15:21:29 -0400 | [diff] [blame] | 334 | mFooter.setKeyguardShowing(keyguardShowing); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 335 | updateQsState(); |
| 336 | } |
| 337 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 338 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 339 | public void setOverscrolling(boolean stackScrollerOverscrolling) { |
| 340 | if (DEBUG) Log.d(TAG, "setOverscrolling " + stackScrollerOverscrolling); |
| 341 | mStackScrollerOverscrolling = stackScrollerOverscrolling; |
| 342 | updateQsState(); |
| 343 | } |
| 344 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 345 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 346 | public void setListening(boolean listening) { |
| 347 | if (DEBUG) Log.d(TAG, "setListening " + listening); |
| 348 | mListening = listening; |
Dave Mankoff | c4fe6c4 | 2019-11-18 17:03:29 -0500 | [diff] [blame] | 349 | mQSContainerImplController.setListening(listening); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 350 | mHeader.setListening(listening); |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 351 | mFooter.setListening(listening); |
Fabian Kozynski | b9dc0c7 | 2018-09-20 13:00:17 -0400 | [diff] [blame] | 352 | mQSPanel.setListening(mListening, mQsExpanded); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 353 | } |
| 354 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 355 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 356 | public void setHeaderListening(boolean listening) { |
| 357 | mHeader.setListening(listening); |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 358 | mFooter.setListening(listening); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 359 | } |
| 360 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 361 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 362 | public void setQsExpansion(float expansion, float headerTranslation) { |
| 363 | if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation); |
| 364 | mContainer.setExpansion(expansion); |
| 365 | final float translationScaleY = expansion - 1; |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 366 | boolean onKeyguardAndExpanded = isKeyguardShowing() && !mShowCollapsedOnKeyguard; |
| 367 | if (!mHeaderAnimating && !headerWillBeAnimating()) { |
Rohan Shah | d3cf756 | 2018-02-23 11:12:28 -0800 | [diff] [blame] | 368 | getView().setTranslationY( |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 369 | onKeyguardAndExpanded |
Rohan Shah | d3cf756 | 2018-02-23 11:12:28 -0800 | [diff] [blame] | 370 | ? translationScaleY * mHeader.getHeight() |
| 371 | : headerTranslation); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 372 | } |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 373 | if (expansion == mLastQSExpansion && mLastKeyguardAndExpanded == onKeyguardAndExpanded) { |
Selim Cinek | 1a891a9 | 2017-12-04 17:41:27 +0100 | [diff] [blame] | 374 | return; |
| 375 | } |
| 376 | mLastQSExpansion = expansion; |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 377 | mLastKeyguardAndExpanded = onKeyguardAndExpanded; |
Rohan Shah | d3cf756 | 2018-02-23 11:12:28 -0800 | [diff] [blame] | 378 | |
| 379 | boolean fullyExpanded = expansion == 1; |
Jason Monk | bdefffd | 2017-06-26 11:22:21 -0400 | [diff] [blame] | 380 | int heightDiff = mQSPanel.getBottom() - mHeader.getBottom() + mHeader.getPaddingBottom() |
| 381 | + mFooter.getHeight(); |
Rohan Shah | d3cf756 | 2018-02-23 11:12:28 -0800 | [diff] [blame] | 382 | float panelTranslationY = translationScaleY * heightDiff; |
| 383 | |
| 384 | // Let the views animate their contents correctly by giving them the necessary context. |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 385 | mHeader.setExpansion(onKeyguardAndExpanded, expansion, |
| 386 | panelTranslationY); |
| 387 | mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion); |
Amin Shaikh | a07a17b | 2018-02-23 16:02:52 -0500 | [diff] [blame] | 388 | mQSPanel.getQsTileRevealController().setExpansion(expansion); |
Amin Shaikh | 0f8ea543 | 2018-03-27 11:09:27 -0400 | [diff] [blame] | 389 | mQSPanel.getTileLayout().setExpansion(expansion); |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 390 | mQSPanel.setTranslationY(translationScaleY * heightDiff); |
Amin Shaikh | acd021e | 2018-01-25 17:25:11 -0500 | [diff] [blame] | 391 | mQSDetail.setFullyExpanded(fullyExpanded); |
Rohan Shah | d3cf756 | 2018-02-23 11:12:28 -0800 | [diff] [blame] | 392 | |
Amin Shaikh | acd021e | 2018-01-25 17:25:11 -0500 | [diff] [blame] | 393 | if (fullyExpanded) { |
| 394 | // Always draw within the bounds of the view when fully expanded. |
| 395 | mQSPanel.setClipBounds(null); |
| 396 | } else { |
| 397 | // Set bounds on the QS panel so it doesn't run over the header when animating. |
| 398 | mQsBounds.top = (int) -mQSPanel.getTranslationY(); |
| 399 | mQsBounds.right = mQSPanel.getWidth(); |
| 400 | mQsBounds.bottom = mQSPanel.getHeight(); |
| 401 | mQSPanel.setClipBounds(mQsBounds); |
| 402 | } |
Anthony Chen | 3cb3ad9 | 2016-12-01 10:58:47 -0800 | [diff] [blame] | 403 | |
| 404 | if (mQSAnimator != null) { |
| 405 | mQSAnimator.setPosition(expansion); |
| 406 | } |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 407 | } |
| 408 | |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 409 | private boolean headerWillBeAnimating() { |
| 410 | return mState == StatusBarState.KEYGUARD && mShowCollapsedOnKeyguard |
| 411 | && !isKeyguardShowing(); |
| 412 | } |
| 413 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 414 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 415 | public void animateHeaderSlidingIn(long delay) { |
| 416 | if (DEBUG) Log.d(TAG, "animateHeaderSlidingIn"); |
| 417 | // If the QS is already expanded we don't need to slide in the header as it's already |
| 418 | // visible. |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 419 | if (!mQsExpanded && getView().getTranslationY() != 0) { |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 420 | mHeaderAnimating = true; |
| 421 | mDelay = delay; |
| 422 | getView().getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn); |
| 423 | } |
| 424 | } |
| 425 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 426 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 427 | public void animateHeaderSlidingOut() { |
| 428 | if (DEBUG) Log.d(TAG, "animateHeaderSlidingOut"); |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 429 | if (getView().getY() == -mHeader.getHeight()) { |
| 430 | return; |
| 431 | } |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 432 | mHeaderAnimating = true; |
| 433 | getView().animate().y(-mHeader.getHeight()) |
| 434 | .setStartDelay(0) |
| 435 | .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD) |
| 436 | .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) |
| 437 | .setListener(new AnimatorListenerAdapter() { |
| 438 | @Override |
| 439 | public void onAnimationEnd(Animator animation) { |
Fabian Kozynski | ae46022 | 2018-09-27 09:36:58 -0400 | [diff] [blame] | 440 | if (getView() != null) { |
| 441 | // The view could be destroyed before the animation completes when |
| 442 | // switching users. |
| 443 | getView().animate().setListener(null); |
| 444 | } |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 445 | mHeaderAnimating = false; |
| 446 | updateQsState(); |
| 447 | } |
| 448 | }) |
| 449 | .start(); |
| 450 | } |
| 451 | |
| 452 | @Override |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 453 | public void setExpandClickListener(OnClickListener onClickListener) { |
Amin Shaikh | 8a3e23c | 2018-04-17 11:14:32 -0400 | [diff] [blame] | 454 | mFooter.setExpandClickListener(onClickListener); |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 455 | } |
| 456 | |
| 457 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 458 | public void closeDetail() { |
| 459 | mQSPanel.closeDetail(); |
| 460 | } |
| 461 | |
| 462 | public void notifyCustomizeChanged() { |
| 463 | // The customize state changed, so our height changed. |
Jason Monk | 4de5d3c | 2017-05-24 14:57:10 -0400 | [diff] [blame] | 464 | mContainer.updateExpansion(); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 465 | mQSPanel.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE); |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 466 | mFooter.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 467 | // Let the panel know the position changed and it needs to update where notifications |
| 468 | // and whatnot are. |
| 469 | mPanelView.onQsHeightChanged(); |
| 470 | } |
| 471 | |
| 472 | /** |
| 473 | * The height this view wants to be. This is different from {@link #getMeasuredHeight} such that |
| 474 | * during closing the detail panel, this already returns the smaller height. |
| 475 | */ |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 476 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 477 | public int getDesiredHeight() { |
| 478 | if (mQSCustomizer.isCustomizing()) { |
| 479 | return getView().getHeight(); |
| 480 | } |
| 481 | if (mQSDetail.isClosingDetail()) { |
Jason Monk | e5b770e | 2017-03-03 21:49:29 -0500 | [diff] [blame] | 482 | LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams(); |
| 483 | int panelHeight = layoutParams.topMargin + layoutParams.bottomMargin + |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 484 | + mQSPanel.getMeasuredHeight(); |
Jason Monk | 232ac2b | 2017-06-02 12:50:24 -0400 | [diff] [blame] | 485 | return panelHeight + getView().getPaddingBottom(); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 486 | } else { |
Jason Monk | 232ac2b | 2017-06-02 12:50:24 -0400 | [diff] [blame] | 487 | return getView().getMeasuredHeight(); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 488 | } |
| 489 | } |
| 490 | |
| 491 | @Override |
| 492 | public void setHeightOverride(int desiredHeight) { |
Jason Monk | 232ac2b | 2017-06-02 12:50:24 -0400 | [diff] [blame] | 493 | mContainer.setHeightOverride(desiredHeight); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 494 | } |
| 495 | |
Anthony Chen | 54daefe | 2017-04-07 17:19:54 -0700 | [diff] [blame] | 496 | @Override |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 497 | public int getQsMinExpansionHeight() { |
Jason Monk | 4de5d3c | 2017-05-24 14:57:10 -0400 | [diff] [blame] | 498 | return mHeader.getHeight(); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 499 | } |
| 500 | |
| 501 | @Override |
| 502 | public void hideImmediately() { |
| 503 | getView().animate().cancel(); |
| 504 | getView().setY(-mHeader.getHeight()); |
| 505 | } |
| 506 | |
| 507 | private final ViewTreeObserver.OnPreDrawListener mStartHeaderSlidingIn |
| 508 | = new ViewTreeObserver.OnPreDrawListener() { |
| 509 | @Override |
| 510 | public boolean onPreDraw() { |
| 511 | getView().getViewTreeObserver().removeOnPreDrawListener(this); |
| 512 | getView().animate() |
| 513 | .translationY(0f) |
| 514 | .setStartDelay(mDelay) |
| 515 | .setDuration(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE) |
| 516 | .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) |
| 517 | .setListener(mAnimateHeaderSlidingInListener) |
| 518 | .start(); |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 519 | return true; |
| 520 | } |
| 521 | }; |
| 522 | |
| 523 | private final Animator.AnimatorListener mAnimateHeaderSlidingInListener |
| 524 | = new AnimatorListenerAdapter() { |
| 525 | @Override |
| 526 | public void onAnimationEnd(Animator animation) { |
| 527 | mHeaderAnimating = false; |
| 528 | updateQsState(); |
| 529 | } |
| 530 | }; |
Selim Cinek | b0fada6 | 2019-06-17 19:03:59 -0700 | [diff] [blame] | 531 | |
| 532 | @Override |
| 533 | public void onStateChanged(int newState) { |
| 534 | mState = newState; |
| 535 | setKeyguardShowing(newState == StatusBarState.KEYGUARD); |
| 536 | } |
Jason Monk | 0ceef21 | 2016-11-02 14:05:23 -0400 | [diff] [blame] | 537 | } |