1 /* 2 * Copyright (C) 2018 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.car; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorInflater; 21 import android.animation.AnimatorListenerAdapter; 22 import android.animation.AnimatorSet; 23 import android.animation.ObjectAnimator; 24 import android.animation.ValueAnimator; 25 import android.app.Fragment; 26 import android.content.Context; 27 import android.os.Bundle; 28 import android.view.LayoutInflater; 29 import android.view.View; 30 import android.view.View.OnClickListener; 31 import android.view.ViewGroup; 32 33 import androidx.annotation.Nullable; 34 import androidx.annotation.VisibleForTesting; 35 import androidx.recyclerview.widget.GridLayoutManager; 36 37 import com.android.systemui.R; 38 import com.android.systemui.plugins.qs.QS; 39 import com.android.systemui.qs.QSFooter; 40 import com.android.systemui.statusbar.car.UserGridRecyclerView; 41 42 import java.util.ArrayList; 43 import java.util.List; 44 45 /** 46 * A quick settings fragment for the car. For auto, there is no row for quick settings or ability 47 * to expand the quick settings panel. Instead, the only thing is that displayed is the 48 * status bar, and a static row with access to the user switcher and settings. 49 */ 50 public class CarQSFragment extends Fragment implements QS { 51 private View mHeader; 52 private View mUserSwitcherContainer; 53 private CarQSFooter mFooter; 54 private View mFooterUserName; 55 private View mFooterExpandIcon; 56 private UserGridRecyclerView mUserGridView; 57 private AnimatorSet mAnimatorSet; 58 private UserSwitchCallback mUserSwitchCallback; 59 60 @Override onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState)61 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 62 Bundle savedInstanceState) { 63 return inflater.inflate(R.layout.car_qs_panel, container, false); 64 } 65 66 @Override onViewCreated(View view, @Nullable Bundle savedInstanceState)67 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 68 super.onViewCreated(view, savedInstanceState); 69 mHeader = view.findViewById(R.id.header); 70 mFooter = view.findViewById(R.id.qs_footer); 71 mFooterUserName = mFooter.findViewById(R.id.user_name); 72 mFooterExpandIcon = mFooter.findViewById(R.id.user_switch_expand_icon); 73 74 mUserSwitcherContainer = view.findViewById(R.id.user_switcher_container); 75 76 updateUserSwitcherHeight(0); 77 78 Context context = getContext(); 79 mUserGridView = mUserSwitcherContainer.findViewById(R.id.user_grid); 80 GridLayoutManager layoutManager = new GridLayoutManager(context, 81 context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col)); 82 mUserGridView.setLayoutManager(layoutManager); 83 mUserGridView.buildAdapter(); 84 85 mUserSwitchCallback = new UserSwitchCallback(); 86 mFooter.setUserSwitchCallback(mUserSwitchCallback); 87 } 88 89 @Override hideImmediately()90 public void hideImmediately() { 91 getView().setVisibility(View.INVISIBLE); 92 } 93 94 @Override setQsExpansion(float qsExpansionFraction, float headerTranslation)95 public void setQsExpansion(float qsExpansionFraction, float headerTranslation) { 96 // If the header is to be completed translated down, then set it to be visible. 97 getView().setVisibility(headerTranslation == 0 ? View.VISIBLE : View.INVISIBLE); 98 } 99 100 @Override getHeader()101 public View getHeader() { 102 return mHeader; 103 } 104 105 @VisibleForTesting getFooter()106 QSFooter getFooter() { 107 return mFooter; 108 } 109 110 @Override setHeaderListening(boolean listening)111 public void setHeaderListening(boolean listening) { 112 mFooter.setListening(listening); 113 } 114 115 @Override setListening(boolean listening)116 public void setListening(boolean listening) { 117 mFooter.setListening(listening); 118 } 119 120 @Override getQsMinExpansionHeight()121 public int getQsMinExpansionHeight() { 122 return getView().getHeight(); 123 } 124 125 @Override getDesiredHeight()126 public int getDesiredHeight() { 127 return getView().getHeight(); 128 } 129 130 @Override setPanelView(HeightListener notificationPanelView)131 public void setPanelView(HeightListener notificationPanelView) { 132 // No quick settings panel. 133 } 134 135 @Override setHeightOverride(int desiredHeight)136 public void setHeightOverride(int desiredHeight) { 137 // No ability to expand quick settings. 138 } 139 140 @Override setHeaderClickable(boolean qsExpansionEnabled)141 public void setHeaderClickable(boolean qsExpansionEnabled) { 142 // Usually this sets the expand button to be clickable, but there is no quick settings to 143 // expand. 144 } 145 146 @Override isCustomizing()147 public boolean isCustomizing() { 148 // No ability to customize the quick settings. 149 return false; 150 } 151 152 @Override setOverscrolling(boolean overscrolling)153 public void setOverscrolling(boolean overscrolling) { 154 // No overscrolling to reveal quick settings. 155 } 156 157 @Override setExpanded(boolean qsExpanded)158 public void setExpanded(boolean qsExpanded) { 159 // No quick settings to expand 160 } 161 162 @Override isShowingDetail()163 public boolean isShowingDetail() { 164 // No detail panel to close. 165 return false; 166 } 167 168 @Override closeDetail()169 public void closeDetail() { 170 // No detail panel to close. 171 } 172 173 @Override animateHeaderSlidingIn(long delay)174 public void animateHeaderSlidingIn(long delay) { 175 // No header to animate. 176 } 177 178 @Override animateHeaderSlidingOut()179 public void animateHeaderSlidingOut() { 180 // No header to animate. 181 } 182 183 @Override notifyCustomizeChanged()184 public void notifyCustomizeChanged() { 185 // There is no ability to customize quick settings. 186 } 187 188 @Override setContainer(ViewGroup container)189 public void setContainer(ViewGroup container) { 190 // No quick settings, so no container to set. 191 } 192 193 @Override setExpandClickListener(OnClickListener onClickListener)194 public void setExpandClickListener(OnClickListener onClickListener) { 195 // No ability to expand the quick settings. 196 } 197 198 public class UserSwitchCallback { 199 private boolean mShowing; 200 isShowing()201 public boolean isShowing() { 202 return mShowing; 203 } 204 show()205 public void show() { 206 mShowing = true; 207 animateHeightChange(true /* opening */); 208 } 209 hide()210 public void hide() { 211 mShowing = false; 212 animateHeightChange(false /* opening */); 213 } 214 } 215 updateUserSwitcherHeight(int height)216 private void updateUserSwitcherHeight(int height) { 217 ViewGroup.LayoutParams layoutParams = mUserSwitcherContainer.getLayoutParams(); 218 layoutParams.height = height; 219 mUserSwitcherContainer.requestLayout(); 220 } 221 animateHeightChange(boolean opening)222 private void animateHeightChange(boolean opening) { 223 // Animation in progress; cancel it to avoid contention. 224 if (mAnimatorSet != null) { 225 mAnimatorSet.cancel(); 226 } 227 228 List<Animator> allAnimators = new ArrayList<>(); 229 ValueAnimator heightAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(getContext(), 230 opening ? R.anim.car_user_switcher_open_animation 231 : R.anim.car_user_switcher_close_animation); 232 heightAnimator.addUpdateListener(valueAnimator -> { 233 updateUserSwitcherHeight((Integer) valueAnimator.getAnimatedValue()); 234 }); 235 allAnimators.add(heightAnimator); 236 237 Animator nameAnimator = AnimatorInflater.loadAnimator(getContext(), 238 opening ? R.anim.car_user_switcher_open_name_animation 239 : R.anim.car_user_switcher_close_name_animation); 240 nameAnimator.setTarget(mFooterUserName); 241 allAnimators.add(nameAnimator); 242 243 Animator iconAnimator = AnimatorInflater.loadAnimator(getContext(), 244 opening ? R.anim.car_user_switcher_open_icon_animation 245 : R.anim.car_user_switcher_close_icon_animation); 246 iconAnimator.setTarget(mFooterExpandIcon); 247 allAnimators.add(iconAnimator); 248 249 mAnimatorSet = new AnimatorSet(); 250 mAnimatorSet.addListener(new AnimatorListenerAdapter() { 251 @Override 252 public void onAnimationEnd(Animator animation) { 253 mAnimatorSet = null; 254 } 255 }); 256 mAnimatorSet.playTogether(allAnimators.toArray(new Animator[0])); 257 258 // Setup all values to the start values in the animations, since there are delays, but need 259 // to have all values start at the beginning. 260 setupInitialValues(mAnimatorSet); 261 262 mAnimatorSet.start(); 263 } 264 setupInitialValues(Animator anim)265 private void setupInitialValues(Animator anim) { 266 if (anim instanceof AnimatorSet) { 267 for (Animator a : ((AnimatorSet) anim).getChildAnimations()) { 268 setupInitialValues(a); 269 } 270 } else if (anim instanceof ObjectAnimator) { 271 ((ObjectAnimator) anim).setCurrentFraction(0.0f); 272 } 273 } 274 } 275