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