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 android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
20 
21 import android.content.Context;
22 import android.content.res.Configuration;
23 import android.database.ContentObserver;
24 import android.net.Uri;
25 import android.os.Build;
26 import android.os.Handler;
27 import android.os.UserHandle;
28 import android.provider.Settings;
29 import android.util.AttributeSet;
30 import android.view.View;
31 import android.widget.FrameLayout;
32 import android.widget.TextView;
33 
34 import androidx.annotation.Nullable;
35 import androidx.annotation.VisibleForTesting;
36 
37 import com.android.settingslib.development.DevelopmentSettingsEnabler;
38 import com.android.systemui.FontSizeUtils;
39 import com.android.systemui.res.R;
40 
41 /**
42  * Footer of expanded Quick Settings, tiles page indicator, (optionally) build number and
43  * {@link FooterActionsView}
44  */
45 public class QSFooterView extends FrameLayout {
46     private PageIndicator mPageIndicator;
47     private TextView mBuildText;
48     private View mEditButton;
49 
50     @Nullable
51     protected TouchAnimator mFooterAnimator;
52 
53     private boolean mQsDisabled;
54     private boolean mExpanded;
55     private float mExpansionAmount;
56 
57     private boolean mShouldShowBuildText;
58 
59     @Nullable
60     private OnClickListener mExpandClickListener;
61 
62     private final ContentObserver mDeveloperSettingsObserver = new ContentObserver(
63             new Handler(mContext.getMainLooper())) {
64         @Override
65         public void onChange(boolean selfChange, Uri uri) {
66             super.onChange(selfChange, uri);
67             setBuildText();
68         }
69     };
70 
QSFooterView(Context context, AttributeSet attrs)71     public QSFooterView(Context context, AttributeSet attrs) {
72         super(context, attrs);
73     }
74 
75     @Override
onFinishInflate()76     protected void onFinishInflate() {
77         super.onFinishInflate();
78         mPageIndicator = findViewById(R.id.footer_page_indicator);
79         mBuildText = findViewById(R.id.build);
80         mEditButton = findViewById(android.R.id.edit);
81 
82         updateResources();
83         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
84         setBuildText();
85     }
86 
setBuildText()87     private void setBuildText() {
88         if (mBuildText == null) return;
89         if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
90             mBuildText.setText(mContext.getString(
91                     com.android.internal.R.string.bugreport_status,
92                     Build.VERSION.RELEASE_OR_CODENAME,
93                     Build.ID));
94             // Set as selected for marquee before its made visible, then it won't be announced when
95             // it's made visible.
96             mBuildText.setSelected(true);
97             mShouldShowBuildText = true;
98         } else {
99             mBuildText.setText(null);
100             mShouldShowBuildText = false;
101             mBuildText.setSelected(false);
102         }
103     }
104 
105     @Override
onConfigurationChanged(Configuration newConfig)106     protected void onConfigurationChanged(Configuration newConfig) {
107         super.onConfigurationChanged(newConfig);
108         updateResources();
109     }
110 
updateResources()111     private void updateResources() {
112         updateFooterAnimator();
113         updateEditButtonResources();
114         updateBuildTextResources();
115         MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
116         lp.height = getResources().getDimensionPixelSize(R.dimen.qs_footer_height);
117         int sideMargin = getResources().getDimensionPixelSize(R.dimen.qs_footer_margin);
118         lp.leftMargin = sideMargin;
119         lp.rightMargin = sideMargin;
120         lp.bottomMargin = getResources().getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
121         setLayoutParams(lp);
122     }
123 
updateEditButtonResources()124     private void updateEditButtonResources() {
125         int size = getResources().getDimensionPixelSize(R.dimen.qs_footer_action_button_size);
126         int padding = getResources().getDimensionPixelSize(R.dimen.qs_footer_icon_padding);
127         MarginLayoutParams lp = (MarginLayoutParams) mEditButton.getLayoutParams();
128         lp.height = size;
129         lp.width = size;
130         mEditButton.setLayoutParams(lp);
131         mEditButton.setPadding(padding, padding, padding, padding);
132     }
133 
updateBuildTextResources()134     private void updateBuildTextResources() {
135         FontSizeUtils.updateFontSizeFromStyle(mBuildText, R.style.TextAppearance_QS_Status_Build);
136     }
137 
updateFooterAnimator()138     private void updateFooterAnimator() {
139         mFooterAnimator = createFooterAnimator();
140     }
141 
142     @Nullable
createFooterAnimator()143     private TouchAnimator createFooterAnimator() {
144         TouchAnimator.Builder builder = new TouchAnimator.Builder()
145                 .addFloat(mPageIndicator, "alpha", 0, 1)
146                 .addFloat(mBuildText, "alpha", 0, 1)
147                 .addFloat(mEditButton, "alpha", 0, 1)
148                 .setStartDelay(0.9f);
149         return builder.build();
150     }
151 
152     /** */
setKeyguardShowing()153     public void setKeyguardShowing() {
154         setExpansion(mExpansionAmount);
155     }
156 
setExpandClickListener(OnClickListener onClickListener)157     public void setExpandClickListener(OnClickListener onClickListener) {
158         mExpandClickListener = onClickListener;
159     }
160 
setExpanded(boolean expanded)161     void setExpanded(boolean expanded) {
162         if (mExpanded == expanded) return;
163         mExpanded = expanded;
164         updateEverything();
165     }
166 
167     /** */
setExpansion(float headerExpansionFraction)168     public void setExpansion(float headerExpansionFraction) {
169         mExpansionAmount = headerExpansionFraction;
170         if (mFooterAnimator != null) {
171             mFooterAnimator.setPosition(headerExpansionFraction);
172         }
173     }
174 
175     @Override
onAttachedToWindow()176     protected void onAttachedToWindow() {
177         super.onAttachedToWindow();
178         mContext.getContentResolver().registerContentObserver(
179                 Settings.Global.getUriFor(Settings.Global.DEVELOPMENT_SETTINGS_ENABLED), false,
180                 mDeveloperSettingsObserver, UserHandle.USER_ALL);
181     }
182 
183     @Override
184     @VisibleForTesting
onDetachedFromWindow()185     public void onDetachedFromWindow() {
186         mContext.getContentResolver().unregisterContentObserver(mDeveloperSettingsObserver);
187         super.onDetachedFromWindow();
188     }
189 
disable(int state2)190     void disable(int state2) {
191         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
192         if (disabled == mQsDisabled) return;
193         mQsDisabled = disabled;
194         updateEverything();
195     }
196 
updateEverything()197     void updateEverything() {
198         post(() -> {
199             updateVisibilities();
200             updateClickabilities();
201             setClickable(false);
202         });
203     }
204 
updateClickabilities()205     private void updateClickabilities() {
206         mBuildText.setLongClickable(mBuildText.getVisibility() == View.VISIBLE);
207     }
208 
updateVisibilities()209     private void updateVisibilities() {
210         mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.INVISIBLE);
211     }
212 }