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