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