1 /* 2 * Copyright (C) 2015 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 package com.android.systemui.qs.customize; 17 18 import android.animation.Animator; 19 import android.animation.Animator.AnimatorListener; 20 import android.animation.AnimatorListenerAdapter; 21 import android.content.Context; 22 import android.content.res.Configuration; 23 import android.util.AttributeSet; 24 import android.util.TypedValue; 25 import android.view.LayoutInflater; 26 import android.view.Menu; 27 import android.view.View; 28 import android.widget.LinearLayout; 29 import android.widget.Toolbar; 30 31 import androidx.recyclerview.widget.DefaultItemAnimator; 32 import androidx.recyclerview.widget.RecyclerView; 33 34 import com.android.systemui.R; 35 import com.android.systemui.plugins.qs.QS; 36 import com.android.systemui.plugins.qs.QSContainerController; 37 import com.android.systemui.qs.QSDetailClipper; 38 import com.android.systemui.statusbar.phone.LightBarController; 39 import com.android.systemui.util.Utils; 40 41 /** 42 * Allows full-screen customization of QS, through show() and hide(). 43 * 44 * This adds itself to the status bar window, so it can appear on top of quick settings and 45 * *someday* do fancy animations to get into/out of it. 46 */ 47 public class QSCustomizer extends LinearLayout { 48 49 static final int MENU_RESET = Menu.FIRST; 50 static final String EXTRA_QS_CUSTOMIZING = "qs_customizing"; 51 52 private final QSDetailClipper mClipper; 53 private final View mTransparentView; 54 55 private boolean isShown; 56 private final RecyclerView mRecyclerView; 57 private boolean mCustomizing; 58 private QSContainerController mQsContainerController; 59 private QS mQs; 60 private int mX; 61 private int mY; 62 private boolean mOpening; 63 private boolean mIsShowingNavBackdrop; 64 QSCustomizer(Context context, AttributeSet attrs)65 public QSCustomizer(Context context, AttributeSet attrs) { 66 super(context, attrs); 67 68 LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this); 69 mClipper = new QSDetailClipper(findViewById(R.id.customize_container)); 70 Toolbar toolbar = findViewById(com.android.internal.R.id.action_bar); 71 TypedValue value = new TypedValue(); 72 mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true); 73 toolbar.setNavigationIcon( 74 getResources().getDrawable(value.resourceId, mContext.getTheme())); 75 76 toolbar.getMenu().add(Menu.NONE, MENU_RESET, 0, 77 mContext.getString(com.android.internal.R.string.reset)); 78 toolbar.setTitle(R.string.qs_edit); 79 mRecyclerView = findViewById(android.R.id.list); 80 mTransparentView = findViewById(R.id.customizer_transparent_view); 81 DefaultItemAnimator animator = new DefaultItemAnimator(); 82 animator.setMoveDuration(TileAdapter.MOVE_DURATION); 83 mRecyclerView.setItemAnimator(animator); 84 } 85 updateResources()86 void updateResources() { 87 LayoutParams lp = (LayoutParams) mTransparentView.getLayoutParams(); 88 lp.height = Utils.getQsHeaderSystemIconsAreaHeight(mContext); 89 mTransparentView.setLayoutParams(lp); 90 } 91 updateNavBackDrop(Configuration newConfig, LightBarController lightBarController)92 void updateNavBackDrop(Configuration newConfig, LightBarController lightBarController) { 93 View navBackdrop = findViewById(R.id.nav_bar_background); 94 mIsShowingNavBackdrop = newConfig.smallestScreenWidthDp >= 600 95 || newConfig.orientation != Configuration.ORIENTATION_LANDSCAPE; 96 if (navBackdrop != null) { 97 navBackdrop.setVisibility(mIsShowingNavBackdrop ? View.VISIBLE : View.GONE); 98 } 99 updateNavColors(lightBarController); 100 } 101 updateNavColors(LightBarController lightBarController)102 void updateNavColors(LightBarController lightBarController) { 103 lightBarController.setQsCustomizing(mIsShowingNavBackdrop && isShown); 104 } 105 setContainerController(QSContainerController controller)106 public void setContainerController(QSContainerController controller) { 107 mQsContainerController = controller; 108 } 109 setQs(QS qs)110 public void setQs(QS qs) { 111 mQs = qs; 112 } 113 114 /** Animate and show QSCustomizer panel. 115 * @param x,y Location on screen of {@code edit} button to determine center of animation. 116 */ show(int x, int y, TileAdapter tileAdapter)117 void show(int x, int y, TileAdapter tileAdapter) { 118 if (!isShown) { 119 int[] containerLocation = findViewById(R.id.customize_container).getLocationOnScreen(); 120 mX = x - containerLocation[0]; 121 mY = y - containerLocation[1]; 122 isShown = true; 123 mOpening = true; 124 setVisibility(View.VISIBLE); 125 mClipper.animateCircularClip(mX, mY, true, new ExpandAnimatorListener(tileAdapter)); 126 mQsContainerController.setCustomizerAnimating(true); 127 mQsContainerController.setCustomizerShowing(true); 128 } 129 } 130 131 showImmediately()132 void showImmediately() { 133 if (!isShown) { 134 setVisibility(VISIBLE); 135 mClipper.cancelAnimator(); 136 mClipper.showBackground(); 137 isShown = true; 138 setCustomizing(true); 139 mQsContainerController.setCustomizerAnimating(false); 140 mQsContainerController.setCustomizerShowing(true); 141 } 142 } 143 144 /** Hide the customizer. */ hide(boolean animate)145 public void hide(boolean animate) { 146 if (isShown) { 147 isShown = false; 148 mClipper.cancelAnimator(); 149 // Make sure we're not opening (because we're closing). Nobody can think we are 150 // customizing after the next two lines. 151 mOpening = false; 152 if (animate) { 153 mClipper.animateCircularClip(mX, mY, false, mCollapseAnimationListener); 154 } else { 155 setVisibility(View.GONE); 156 } 157 mQsContainerController.setCustomizerAnimating(animate); 158 mQsContainerController.setCustomizerShowing(false); 159 } 160 } 161 isShown()162 public boolean isShown() { 163 return isShown; 164 } 165 setCustomizing(boolean customizing)166 void setCustomizing(boolean customizing) { 167 mCustomizing = customizing; 168 mQs.notifyCustomizeChanged(); 169 } 170 isCustomizing()171 public boolean isCustomizing() { 172 return mCustomizing || mOpening; 173 } 174 175 /** @param x,y Location on screen of animation center. 176 */ setEditLocation(int x, int y)177 public void setEditLocation(int x, int y) { 178 int[] containerLocation = findViewById(R.id.customize_container).getLocationOnScreen(); 179 mX = x - containerLocation[0]; 180 mY = y - containerLocation[1]; 181 } 182 183 class ExpandAnimatorListener extends AnimatorListenerAdapter { 184 private final TileAdapter mTileAdapter; 185 ExpandAnimatorListener(TileAdapter tileAdapter)186 ExpandAnimatorListener(TileAdapter tileAdapter) { 187 mTileAdapter = tileAdapter; 188 } 189 190 @Override onAnimationEnd(Animator animation)191 public void onAnimationEnd(Animator animation) { 192 if (isShown) { 193 setCustomizing(true); 194 } 195 mOpening = false; 196 mQsContainerController.setCustomizerAnimating(false); 197 mRecyclerView.setAdapter(mTileAdapter); 198 } 199 200 @Override onAnimationCancel(Animator animation)201 public void onAnimationCancel(Animator animation) { 202 mOpening = false; 203 mQs.notifyCustomizeChanged(); 204 mQsContainerController.setCustomizerAnimating(false); 205 } 206 } 207 208 private final AnimatorListener mCollapseAnimationListener = new AnimatorListenerAdapter() { 209 @Override 210 public void onAnimationEnd(Animator animation) { 211 if (!isShown) { 212 setVisibility(View.GONE); 213 } 214 mQsContainerController.setCustomizerAnimating(false); 215 } 216 217 @Override 218 public void onAnimationCancel(Animator animation) { 219 if (!isShown) { 220 setVisibility(View.GONE); 221 } 222 mQsContainerController.setCustomizerAnimating(false); 223 } 224 }; 225 getRecyclerView()226 public RecyclerView getRecyclerView() { 227 return mRecyclerView; 228 } 229 isOpening()230 public boolean isOpening() { 231 return mOpening; 232 } 233 }