1 /*
2  * Copyright (C) 2023 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.car.statusicon;
18 
19 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
20 import static android.widget.ListPopupWindow.WRAP_CONTENT;
21 
22 import android.annotation.DimenRes;
23 import android.annotation.LayoutRes;
24 import android.app.PendingIntent;
25 import android.car.app.CarActivityManager;
26 import android.car.drivingstate.CarUxRestrictions;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.graphics.Outline;
32 import android.graphics.drawable.Drawable;
33 import android.view.Gravity;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.view.ViewOutlineProvider;
38 import android.view.ViewTreeObserver;
39 import android.view.WindowManager;
40 import android.widget.PopupWindow;
41 import android.widget.Toast;
42 
43 import androidx.annotation.NonNull;
44 import androidx.annotation.VisibleForTesting;
45 
46 import com.android.car.qc.QCItem;
47 import com.android.car.qc.view.QCView;
48 import com.android.car.ui.FocusParkingView;
49 import com.android.car.ui.utils.CarUxRestrictionsUtil;
50 import com.android.systemui.R;
51 import com.android.systemui.broadcast.BroadcastDispatcher;
52 import com.android.systemui.car.CarDeviceProvisionedController;
53 import com.android.systemui.car.CarServiceProvider;
54 import com.android.systemui.car.qc.SystemUIQCViewController;
55 import com.android.systemui.car.systembar.element.CarSystemBarElementController;
56 import com.android.systemui.car.systembar.element.CarSystemBarElementInitializer;
57 import com.android.systemui.car.users.CarSystemUIUserUtil;
58 import com.android.systemui.settings.UserTracker;
59 import com.android.systemui.statusbar.policy.ConfigurationController;
60 import com.android.systemui.util.ViewController;
61 
62 import java.util.ArrayList;
63 import java.util.List;
64 
65 import javax.inject.Inject;
66 
67 /**
68  * A controller for a panel view associated with a status icon.
69  */
70 public class StatusIconPanelViewController extends ViewController<View> {
71     private final Context mContext;
72     private final UserTracker mUserTracker;
73     private final CarServiceProvider mCarServiceProvider;
74     private final BroadcastDispatcher mBroadcastDispatcher;
75     private final ConfigurationController mConfigurationController;
76     private final CarDeviceProvisionedController mCarDeviceProvisionedController;
77     private final CarSystemBarElementInitializer mCarSystemBarElementInitializer;
78     private final String mIdentifier;
79     @LayoutRes
80     private final int mPanelLayoutRes;
81     @DimenRes
82     private final int mPanelWidthRes;
83     private final int mXOffsetPixel;
84     private final int mYOffsetPixel;
85     private final int mPanelGravity;
86     private final boolean mIsDisabledWhileDriving;
87     private final boolean mIsDisabledWhileUnprovisioned;
88     private final boolean mShowAsDropDown;
89     private final ArrayList<SystemUIQCViewController> mQCViewControllers = new ArrayList<>();
90 
91     private PopupWindow mPanel;
92     private ViewGroup mPanelContent;
93     private CarUxRestrictionsUtil mCarUxRestrictionsUtil;
94     private CarActivityManager mCarActivityManager;
95     private float mDimValue = -1.0f;
96     private View.OnClickListener mOnClickListener;
97 
98     private final ConfigurationController.ConfigurationListener mConfigurationListener =
99             new ConfigurationController.ConfigurationListener() {
100                 @Override
101                 public void onLayoutDirectionChanged(boolean isLayoutRtl) {
102                     recreatePanel();
103                 }
104             };
105 
106     private final View.OnLayoutChangeListener mPanelContentLayoutChangeListener =
107             new View.OnLayoutChangeListener() {
108                 @Override
109                 public void onLayoutChange(View v, int left, int top, int right, int bottom,
110                         int oldLeft, int oldTop, int oldRight, int oldBottom) {
111                     if (mPanelContent != null) {
112                         mPanelContent.invalidateOutline();
113                     }
114                 }
115             };
116 
117     private final CarUxRestrictionsUtil.OnUxRestrictionsChangedListener
118             mUxRestrictionsChangedListener =
119             new CarUxRestrictionsUtil.OnUxRestrictionsChangedListener() {
120                 @Override
121                 public void onRestrictionsChanged(@NonNull CarUxRestrictions carUxRestrictions) {
122                     if (mIsDisabledWhileDriving
123                             && carUxRestrictions.isRequiresDistractionOptimization()
124                             && isPanelShowing()) {
125                         mPanel.dismiss();
126                     }
127                 }
128             };
129 
130     private final CarServiceProvider.CarServiceOnConnectedListener mCarServiceOnConnectedListener =
131             car -> {
132                 mCarActivityManager = car.getCarManager(CarActivityManager.class);
133             };
134 
135     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
136         @Override
137         public void onReceive(Context context, Intent intent) {
138             String action = intent.getAction();
139             boolean isIntentFromSelf =
140                     intent.getIdentifier() != null && intent.getIdentifier().equals(mIdentifier);
141 
142             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) && !isIntentFromSelf
143                     && isPanelShowing()) {
144                 mPanel.dismiss();
145             }
146         }
147     };
148 
149     private final UserTracker.Callback mUserTrackerCallback = new UserTracker.Callback() {
150         @Override
151         public void onUserChanged(int newUser, Context userContext) {
152             mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
153             mBroadcastDispatcher.registerReceiver(mBroadcastReceiver,
154                     new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), /* executor= */ null,
155                     mUserTracker.getUserHandle());
156         }
157     };
158 
159     private final ViewTreeObserver.OnGlobalFocusChangeListener mFocusChangeListener =
160             (oldFocus, newFocus) -> {
161                 if (isPanelShowing() && oldFocus != null && newFocus instanceof FocusParkingView) {
162                     // When nudging out of the panel, RotaryService will focus on the
163                     // FocusParkingView to clear the focus highlight. When this occurs, dismiss the
164                     // panel.
165                     mPanel.dismiss();
166                 }
167             };
168 
169     private final QCView.QCActionListener mQCActionListener = (item, action) -> {
170         if (!isPanelShowing()) {
171             return;
172         }
173         if (action instanceof PendingIntent) {
174             if (((PendingIntent) action).isActivity()) {
175                 mPanel.dismiss();
176             }
177         } else if (action instanceof QCItem.ActionHandler) {
178             if (((QCItem.ActionHandler) action).isActivity()) {
179                 mPanel.dismiss();
180             }
181         }
182     };
183 
StatusIconPanelViewController(Context context, UserTracker userTracker, CarServiceProvider carServiceProvider, BroadcastDispatcher broadcastDispatcher, ConfigurationController configurationController, CarDeviceProvisionedController deviceProvisionedController, CarSystemBarElementInitializer elementInitializer, View anchorView, @LayoutRes int layoutRes, @DimenRes int widthRes, int xOffset, int yOffset, int gravity, boolean isDisabledWhileDriving, boolean isDisabledWhileUnprovisioned, boolean showAsDropDown)184     private StatusIconPanelViewController(Context context,
185             UserTracker userTracker,
186             CarServiceProvider carServiceProvider,
187             BroadcastDispatcher broadcastDispatcher,
188             ConfigurationController configurationController,
189             CarDeviceProvisionedController deviceProvisionedController,
190             CarSystemBarElementInitializer elementInitializer,
191             View anchorView, @LayoutRes int layoutRes, @DimenRes int widthRes,
192             int xOffset, int yOffset, int gravity, boolean isDisabledWhileDriving,
193             boolean isDisabledWhileUnprovisioned, boolean showAsDropDown) {
194         super(anchorView);
195         mContext = context;
196         mUserTracker = userTracker;
197         mCarServiceProvider = carServiceProvider;
198         mBroadcastDispatcher = broadcastDispatcher;
199         mConfigurationController = configurationController;
200         mCarDeviceProvisionedController = deviceProvisionedController;
201         mCarSystemBarElementInitializer = elementInitializer;
202         mIsDisabledWhileDriving = isDisabledWhileDriving;
203         mIsDisabledWhileUnprovisioned = isDisabledWhileUnprovisioned;
204         mPanelLayoutRes = layoutRes;
205         mPanelWidthRes = widthRes;
206         mXOffsetPixel = xOffset;
207         mYOffsetPixel = yOffset;
208         mPanelGravity = gravity;
209         mShowAsDropDown = showAsDropDown;
210         mIdentifier = Integer.toString(System.identityHashCode(this));
211     }
212 
213     @Override
onInit()214     protected void onInit() {
215         mOnClickListener = v -> {
216             if (mIsDisabledWhileUnprovisioned && !isDeviceSetupForUser()) {
217                 return;
218             }
219             if (mIsDisabledWhileDriving && mCarUxRestrictionsUtil.getCurrentRestrictions()
220                     .isRequiresDistractionOptimization()) {
221                 dismissAllSystemDialogs();
222                 Toast.makeText(mContext, R.string.car_ui_restricted_while_driving,
223                         Toast.LENGTH_LONG).show();
224                 return;
225             }
226 
227             if (mPanel == null && !createPanel()) {
228                 return;
229             }
230 
231             if (mPanel.isShowing()) {
232                 mPanel.dismiss();
233                 return;
234             }
235 
236             // Dismiss all currently open system dialogs before opening this panel.
237             dismissAllSystemDialogs();
238 
239             registerFocusListener(true);
240 
241             if (CarSystemUIUserUtil.isMUMDSystemUI()
242                     && mPanelLayoutRes == R.layout.qc_profile_switcher) {
243                 // TODO(b/269490856): consider removal of UserPicker carve-outs
244                 if (mCarActivityManager != null) {
245                     mCarActivityManager.startUserPickerOnDisplay(mContext.getDisplayId());
246                 }
247             } else {
248                 if (mShowAsDropDown) {
249                     // TODO(b/202563671): remove yOffsetPixel when the PopupWindow API is updated.
250                     mPanel.showAsDropDown(mView, mXOffsetPixel, mYOffsetPixel, mPanelGravity);
251                 } else {
252                     int verticalGravity = mPanelGravity & Gravity.VERTICAL_GRAVITY_MASK;
253                     int animationStyle = verticalGravity == Gravity.BOTTOM
254                             ? com.android.internal.R.style.Animation_DropDownUp
255                             : com.android.internal.R.style.Animation_DropDownDown;
256                     mPanel.setAnimationStyle(animationStyle);
257                     mPanel.showAtLocation(mView, mPanelGravity, mXOffsetPixel, mYOffsetPixel);
258                 }
259                 mView.setSelected(true);
260                 setAnimatedStatusIconHighlightedStatus(true);
261                 dimBehind(mPanel);
262             }
263         };
264 
265         mView.setOnClickListener(mOnClickListener);
266     }
267 
268     @Override
onViewAttached()269     protected void onViewAttached() {
270         if (mPanel == null) {
271             createPanel();
272         }
273         mBroadcastDispatcher.registerReceiver(mBroadcastReceiver,
274                 new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), /* executor= */ null,
275                 mUserTracker.getUserHandle());
276         mUserTracker.addCallback(mUserTrackerCallback, mContext.getMainExecutor());
277         mConfigurationController.addCallback(mConfigurationListener);
278 
279         if (mIsDisabledWhileDriving) {
280             mCarUxRestrictionsUtil = CarUxRestrictionsUtil.getInstance(mContext);
281             mCarUxRestrictionsUtil.register(mUxRestrictionsChangedListener);
282         }
283         mCarServiceProvider.addListener(mCarServiceOnConnectedListener);
284     }
285 
286     @Override
onViewDetached()287     protected void onViewDetached() {
288         reset();
289         if (mCarUxRestrictionsUtil != null) {
290             mCarUxRestrictionsUtil.unregister(mUxRestrictionsChangedListener);
291         }
292         mCarServiceProvider.removeListener(mCarServiceOnConnectedListener);
293         mConfigurationController.removeCallback(mConfigurationListener);
294         mUserTracker.removeCallback(mUserTrackerCallback);
295         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
296     }
297 
298     @VisibleForTesting
getPanel()299     PopupWindow getPanel() {
300         return mPanel;
301     }
302 
303     @VisibleForTesting
getBroadcastReceiver()304     BroadcastReceiver getBroadcastReceiver() {
305         return mBroadcastReceiver;
306     }
307 
308     @VisibleForTesting
getIdentifier()309     String getIdentifier() {
310         return mIdentifier;
311     }
312 
313     @VisibleForTesting
getOnClickListener()314     View.OnClickListener getOnClickListener() {
315         return mOnClickListener;
316     }
317 
318     @VisibleForTesting
getConfigurationListener()319     ConfigurationController.ConfigurationListener getConfigurationListener() {
320         return mConfigurationListener;
321     }
322 
323     @VisibleForTesting
getUserTrackerCallback()324     UserTracker.Callback getUserTrackerCallback() {
325         return mUserTrackerCallback;
326     }
327 
328     @VisibleForTesting
getFocusChangeListener()329     ViewTreeObserver.OnGlobalFocusChangeListener getFocusChangeListener() {
330         return mFocusChangeListener;
331     }
332 
333     @VisibleForTesting
getQCActionListener()334     QCView.QCActionListener getQCActionListener() {
335         return mQCActionListener;
336     }
337 
338     /**
339      * Create the PopupWindow panel and assign to {@link mPanel}.
340      * @return true if the panel was created, false otherwise
341      */
createPanel()342     private boolean createPanel() {
343         if (mPanelWidthRes == 0 || mPanelLayoutRes == 0) {
344             return false;
345         }
346 
347         int panelWidth = mContext.getResources().getDimensionPixelSize(mPanelWidthRes);
348         Drawable panelBackgroundDrawable = mContext.getResources()
349                 .getDrawable(R.drawable.status_icon_panel_bg, mContext.getTheme());
350         mPanelContent = (ViewGroup) LayoutInflater.from(mContext).inflate(mPanelLayoutRes,
351                 /* root= */ null);
352         // clip content to the panel background (to handle rounded corners)
353         mPanelContent.setOutlineProvider(new DrawableViewOutlineProvider(panelBackgroundDrawable));
354         mPanelContent.setClipToOutline(true);
355         mPanelContent.addOnLayoutChangeListener(mPanelContentLayoutChangeListener);
356 
357         // initialize special views
358         initQCElementViews(mPanelContent);
359 
360         // initialize panel
361         mPanel = new PopupWindow(mPanelContent, panelWidth, WRAP_CONTENT);
362         mPanel.setBackgroundDrawable(panelBackgroundDrawable);
363         mPanel.setWindowLayoutType(TYPE_SYSTEM_DIALOG);
364         mPanel.setFocusable(true);
365         mPanel.setOutsideTouchable(false);
366         mPanel.setOnDismissListener(() -> {
367             setAnimatedStatusIconHighlightedStatus(false);
368             mView.setSelected(false);
369             registerFocusListener(false);
370         });
371 
372         return true;
373     }
374 
dimBehind(PopupWindow popupWindow)375     private void dimBehind(PopupWindow popupWindow) {
376         View container = popupWindow.getContentView().getRootView();
377         WindowManager wm = mContext.getSystemService(WindowManager.class);
378 
379         if (wm == null) return;
380 
381         if (mDimValue < 0) {
382             mDimValue = mContext.getResources().getFloat(R.dimen.car_status_icon_panel_dim);
383         }
384 
385         WindowManager.LayoutParams lp = (WindowManager.LayoutParams) container.getLayoutParams();
386         lp.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
387         lp.dimAmount = mDimValue;
388         wm.updateViewLayout(container, lp);
389     }
390 
dismissAllSystemDialogs()391     private void dismissAllSystemDialogs() {
392         Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
393         intent.setIdentifier(mIdentifier);
394         mContext.sendBroadcastAsUser(intent, mUserTracker.getUserHandle());
395     }
396 
registerFocusListener(boolean register)397     private void registerFocusListener(boolean register) {
398         if (mPanelContent == null) {
399             return;
400         }
401         if (register) {
402             mPanelContent.getViewTreeObserver().addOnGlobalFocusChangeListener(
403                     mFocusChangeListener);
404         } else {
405             mPanelContent.getViewTreeObserver().removeOnGlobalFocusChangeListener(
406                     mFocusChangeListener);
407         }
408     }
409 
reset()410     private void reset() {
411         if (mPanel == null) return;
412 
413         mPanel.dismiss();
414         mPanel = null;
415         if (mPanelContent != null) {
416             mPanelContent.removeOnLayoutChangeListener(mPanelContentLayoutChangeListener);
417         }
418         mPanelContent = null;
419         mQCViewControllers.forEach(SystemUIQCViewController::destroyQCViews);
420         mQCViewControllers.clear();
421     }
422 
recreatePanel()423     private void recreatePanel() {
424         reset();
425         createPanel();
426     }
427 
initQCElementViews(ViewGroup rootView)428     private void initQCElementViews(ViewGroup rootView) {
429         List<CarSystemBarElementController> controllers =
430                 mCarSystemBarElementInitializer.initializeCarSystemBarElements(rootView);
431         for (CarSystemBarElementController controller : controllers) {
432             if (controller instanceof SystemUIQCViewController) {
433                 SystemUIQCViewController qcController = (SystemUIQCViewController) controller;
434                 qcController.setActionListener(mQCActionListener);
435                 mQCViewControllers.add(qcController);
436             }
437         }
438     }
439 
findViewsOfType(ViewGroup rootView, Class<T> clazz)440     private <T extends View> List<T> findViewsOfType(ViewGroup rootView, Class<T> clazz) {
441         List<T> views = new ArrayList<>();
442         for (int i = 0; i < rootView.getChildCount(); i++) {
443             View v = rootView.getChildAt(i);
444             if (clazz.isInstance(v)) {
445                 views.add(clazz.cast(v));
446             } else if (v instanceof ViewGroup) {
447                 views.addAll(findViewsOfType((ViewGroup) v, clazz));
448             }
449         }
450         return views;
451     }
452 
setAnimatedStatusIconHighlightedStatus(boolean isHighlighted)453     private void setAnimatedStatusIconHighlightedStatus(boolean isHighlighted) {
454         if (mView instanceof AnimatedStatusIcon) {
455             ((AnimatedStatusIcon) mView).setIconHighlighted(isHighlighted);
456         }
457     }
458 
isPanelShowing()459     private boolean isPanelShowing() {
460         return mPanel != null && mPanel.isShowing();
461     }
462 
isDeviceSetupForUser()463     private boolean isDeviceSetupForUser() {
464         return mCarDeviceProvisionedController.isCurrentUserSetup()
465                 && !mCarDeviceProvisionedController.isCurrentUserSetupInProgress();
466     }
467 
468     private static class DrawableViewOutlineProvider extends ViewOutlineProvider {
469         private final Drawable mDrawable;
470 
DrawableViewOutlineProvider(Drawable drawable)471         private DrawableViewOutlineProvider(Drawable drawable) {
472             mDrawable = drawable;
473         }
474 
475         @Override
getOutline(View view, Outline outline)476         public void getOutline(View view, Outline outline) {
477             if (mDrawable != null) {
478                 mDrawable.getOutline(outline);
479             } else {
480                 outline.setRect(0, 0, view.getWidth(), view.getHeight());
481                 outline.setAlpha(0.0f);
482             }
483         }
484     }
485 
486     /** Daggerized builder for StatusIconPanelViewController */
487     public static class Builder {
488         private final Context mContext;
489         private final UserTracker mUserTracker;
490         private final CarServiceProvider mCarServiceProvider;
491         private final BroadcastDispatcher mBroadcastDispatcher;
492         private final ConfigurationController mConfigurationController;
493         private final CarDeviceProvisionedController mCarDeviceProvisionedController;
494         private final CarSystemBarElementInitializer mCarSystemBarElementInitializer;
495 
496         private int mXOffset = 0;
497         private int mYOffset;
498         private int mGravity = Gravity.TOP | Gravity.START;
499         private boolean mIsDisabledWhileDriving = false;
500         private boolean mIsDisabledWhileUnprovisioned = false;
501         private boolean mShowAsDropDown = true;
502 
503         @Inject
Builder( Context context, UserTracker userTracker, CarServiceProvider carServiceProvider, BroadcastDispatcher broadcastDispatcher, ConfigurationController configurationController, CarDeviceProvisionedController deviceProvisionedController, CarSystemBarElementInitializer elementInitializer)504         public Builder(
505                 Context context,
506                 UserTracker userTracker,
507                 CarServiceProvider carServiceProvider,
508                 BroadcastDispatcher broadcastDispatcher,
509                 ConfigurationController configurationController,
510                 CarDeviceProvisionedController deviceProvisionedController,
511                 CarSystemBarElementInitializer elementInitializer) {
512             mContext = context;
513             mUserTracker = userTracker;
514             mCarServiceProvider = carServiceProvider;
515             mBroadcastDispatcher = broadcastDispatcher;
516             mConfigurationController = configurationController;
517             mCarDeviceProvisionedController = deviceProvisionedController;
518             mCarSystemBarElementInitializer = elementInitializer;
519 
520             int panelMarginTop = mContext.getResources().getDimensionPixelSize(
521                     R.dimen.car_status_icon_panel_margin_top);
522             int topSystemBarHeight = mContext.getResources().getDimensionPixelSize(
523                     R.dimen.car_top_system_bar_height);
524             // TODO(b/202563671): remove mYOffset when the PopupWindow API is updated.
525             mYOffset = panelMarginTop - topSystemBarHeight;
526         }
527 
528         /** Set the panel offset in the x direction by a specified number of pixels. */
setXOffset(int offset)529         public Builder setXOffset(int offset) {
530             mXOffset = offset;
531             return this;
532         }
533 
534         /** Set the panel offset in the y direction by a specified number of pixels. */
setYOffset(int offset)535         public Builder setYOffset(int offset) {
536             mYOffset = offset;
537             return this;
538         }
539 
540         /** Set the panel's gravity - by default the gravity will be `Gravity.TOP | Gravity.START`*/
setGravity(int gravity)541         public Builder setGravity(int gravity) {
542             mGravity = gravity;
543             return this;
544         }
545 
546         /** Set whether the panel should be shown while driving or not - defaults to false. */
setDisabledWhileDriving(boolean disabled)547         public Builder setDisabledWhileDriving(boolean disabled) {
548             mIsDisabledWhileDriving = disabled;
549             return this;
550         }
551 
552         /**
553          * Sets whether the panel should be disabled when the device is unprovisioned - defaults
554          * to false
555          */
setDisabledWhileUnprovisioned(boolean disabled)556         public Builder setDisabledWhileUnprovisioned(boolean disabled) {
557             mIsDisabledWhileUnprovisioned = disabled;
558             return this;
559         }
560 
561         /**
562          * Set whether the panel should be shown as a dropdown (vs. at a specific location)
563          * - defaults to true.
564          */
setShowAsDropDown(boolean dropDown)565         public Builder setShowAsDropDown(boolean dropDown) {
566             mShowAsDropDown = dropDown;
567             return this;
568         }
569 
570         /**
571          * Builds the controller with the required parameters of anchor view, panel layout resource,
572          * and panel width resources.
573          */
build(View anchorView, @LayoutRes int layoutRes, @DimenRes int widthRes)574         public StatusIconPanelViewController build(View anchorView, @LayoutRes int layoutRes,
575                 @DimenRes int widthRes) {
576             return new StatusIconPanelViewController(mContext, mUserTracker, mCarServiceProvider,
577                     mBroadcastDispatcher, mConfigurationController, mCarDeviceProvisionedController,
578                     mCarSystemBarElementInitializer, anchorView, layoutRes, widthRes, mXOffset,
579                     mYOffset, mGravity, mIsDisabledWhileDriving, mIsDisabledWhileUnprovisioned,
580                     mShowAsDropDown);
581         }
582     }
583 }
584