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 }