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 setKeyguardShowing(boolean keyguardShowing)174 public void setKeyguardShowing(boolean keyguardShowing) { 175 // No keyguard to show. 176 } 177 178 @Override animateHeaderSlidingIn(long delay)179 public void animateHeaderSlidingIn(long delay) { 180 // No header to animate. 181 } 182 183 @Override animateHeaderSlidingOut()184 public void animateHeaderSlidingOut() { 185 // No header to animate. 186 } 187 188 @Override notifyCustomizeChanged()189 public void notifyCustomizeChanged() { 190 // There is no ability to customize quick settings. 191 } 192 193 @Override setContainer(ViewGroup container)194 public void setContainer(ViewGroup container) { 195 // No quick settings, so no container to set. 196 } 197 198 @Override setExpandClickListener(OnClickListener onClickListener)199 public void setExpandClickListener(OnClickListener onClickListener) { 200 // No ability to expand the quick settings. 201 } 202 203 public class UserSwitchCallback { 204 private boolean mShowing; 205 isShowing()206 public boolean isShowing() { 207 return mShowing; 208 } 209 show()210 public void show() { 211 mShowing = true; 212 animateHeightChange(true /* opening */); 213 } 214 hide()215 public void hide() { 216 mShowing = false; 217 animateHeightChange(false /* opening */); 218 } 219 } 220 updateUserSwitcherHeight(int height)221 private void updateUserSwitcherHeight(int height) { 222 ViewGroup.LayoutParams layoutParams = mUserSwitcherContainer.getLayoutParams(); 223 layoutParams.height = height; 224 mUserSwitcherContainer.requestLayout(); 225 } 226 animateHeightChange(boolean opening)227 private void animateHeightChange(boolean opening) { 228 // Animation in progress; cancel it to avoid contention. 229 if (mAnimatorSet != null) { 230 mAnimatorSet.cancel(); 231 } 232 233 List<Animator> allAnimators = new ArrayList<>(); 234 ValueAnimator heightAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(getContext(), 235 opening ? R.anim.car_user_switcher_open_animation 236 : R.anim.car_user_switcher_close_animation); 237 heightAnimator.addUpdateListener(valueAnimator -> { 238 updateUserSwitcherHeight((Integer) valueAnimator.getAnimatedValue()); 239 }); 240 allAnimators.add(heightAnimator); 241 242 Animator nameAnimator = AnimatorInflater.loadAnimator(getContext(), 243 opening ? R.anim.car_user_switcher_open_name_animation 244 : R.anim.car_user_switcher_close_name_animation); 245 nameAnimator.setTarget(mFooterUserName); 246 allAnimators.add(nameAnimator); 247 248 Animator iconAnimator = AnimatorInflater.loadAnimator(getContext(), 249 opening ? R.anim.car_user_switcher_open_icon_animation 250 : R.anim.car_user_switcher_close_icon_animation); 251 iconAnimator.setTarget(mFooterExpandIcon); 252 allAnimators.add(iconAnimator); 253 254 mAnimatorSet = new AnimatorSet(); 255 mAnimatorSet.addListener(new AnimatorListenerAdapter() { 256 @Override 257 public void onAnimationEnd(Animator animation) { 258 mAnimatorSet = null; 259 } 260 }); 261 mAnimatorSet.playTogether(allAnimators.toArray(new Animator[0])); 262 263 // Setup all values to the start values in the animations, since there are delays, but need 264 // to have all values start at the beginning. 265 setupInitialValues(mAnimatorSet); 266 267 mAnimatorSet.start(); 268 } 269 setupInitialValues(Animator anim)270 private void setupInitialValues(Animator anim) { 271 if (anim instanceof AnimatorSet) { 272 for (Animator a : ((AnimatorSet) anim).getChildAnimations()) { 273 setupInitialValues(a); 274 } 275 } else if (anim instanceof ObjectAnimator) { 276 ((ObjectAnimator) anim).setCurrentFraction(0.0f); 277 } 278 } 279 } 280