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.chassis.car.ui.plugin.toolbar;
18 
19 import static com.android.car.ui.utils.CarUiUtils.requireViewByRefId;
20 
21 import android.text.TextUtils;
22 import android.view.View;
23 import android.view.ViewGroup;
24 import android.widget.ImageView;
25 import android.widget.Switch;
26 import android.widget.TextView;
27 
28 import androidx.annotation.LayoutRes;
29 import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
30 
31 import com.android.car.ui.toolbar.MenuItem;
32 import com.android.car.ui.uxr.DrawableStateView;
33 
34 import com.chassis.car.ui.plugin.R;
35 
36 import java.util.function.Consumer;
37 
38 class MenuItemRenderer implements MenuItem.Listener {
39 
40     private static final int[] RESTRICTED_STATE = new int[]{R.attr.state_ux_restricted};
41 
42     private final int mMenuItemIconSize;
43 
44     private boolean mToolbarIsSearching;
45 
46     private final MenuItem mMenuItem;
47     private final ViewGroup mParentView;
48     private View mView;
49     private View mIconContainer;
50     private ImageView mIconView;
51     private Switch mSwitch;
52     private TextView mTextView;
53     private TextView mTextWithIconView;
54     private boolean mIndividualClickListeners;
55 
MenuItemRenderer(MenuItem item, ViewGroup parentView)56     MenuItemRenderer(MenuItem item, ViewGroup parentView) {
57         mMenuItem = item;
58         mParentView = parentView;
59         mMenuItem.setListener(this);
60 
61         mMenuItemIconSize = parentView.getContext().getResources()
62                 .getDimensionPixelSize(R.dimen.car_ui_toolbar_menu_item_icon_size);
63     }
64 
setToolbarIsSearching(boolean searching)65     void setToolbarIsSearching(boolean searching) {
66         if (searching != mToolbarIsSearching) {
67             mToolbarIsSearching = searching;
68 
69             if (mMenuItem.isSearch()) {
70                 updateView();
71             }
72         }
73     }
74 
75     @Override
onMenuItemChanged(MenuItem changedItem)76     public void onMenuItemChanged(MenuItem changedItem) {
77         updateView();
78     }
79 
createView(Consumer<View> callback)80     void createView(Consumer<View> callback) {
81         AsyncLayoutInflater inflater = new AsyncLayoutInflater(mParentView.getContext());
82         @LayoutRes int layout = mMenuItem.isPrimary()
83                 ? R.layout.car_ui_toolbar_menu_item_primary
84                 : R.layout.car_ui_toolbar_menu_item;
85         inflater.inflate(layout, mParentView, (View view, int resid,
86                 ViewGroup parent) -> {
87             mView = view;
88 
89             mIconContainer =
90                     requireViewByRefId(mView, R.id.car_ui_toolbar_menu_item_icon_container);
91             mIconView = requireViewByRefId(mView, R.id.car_ui_toolbar_menu_item_icon);
92             mSwitch = requireViewByRefId(mView, R.id.car_ui_toolbar_menu_item_switch);
93             mTextView = requireViewByRefId(mView, R.id.car_ui_toolbar_menu_item_text);
94             mTextWithIconView =
95                     requireViewByRefId(mView, R.id.car_ui_toolbar_menu_item_text_with_icon);
96             mIndividualClickListeners = mView.getContext().getResources()
97                     .getBoolean(R.bool.car_ui_toolbar_menuitem_individual_click_listeners);
98 
99             updateView();
100             callback.accept(mView);
101         });
102     }
103 
updateView()104     private void updateView() {
105         if (mView == null) {
106             return;
107         }
108 
109         mView.setId(mMenuItem.getId());
110 
111         boolean hasIcon = mMenuItem.getIcon() != null;
112         boolean hasText = !TextUtils.isEmpty(mMenuItem.getTitle());
113         boolean textAndIcon = mMenuItem.isShowingIconAndTitle();
114         boolean checkable = mMenuItem.isCheckable();
115 
116         if (!mMenuItem.isVisible()
117                 || (mMenuItem.isSearch() && mToolbarIsSearching)
118                 || (!checkable && !hasIcon && !hasText)) {
119             mView.setVisibility(View.GONE);
120             return;
121         }
122         mView.setVisibility(View.VISIBLE);
123         mView.setContentDescription(mMenuItem.getTitle());
124 
125         View clickTarget;
126         if (checkable) {
127             mSwitch.setChecked(mMenuItem.isChecked());
128             clickTarget = mSwitch;
129         } else if (hasText && hasIcon && textAndIcon) {
130             mMenuItem.getIcon().setBounds(0, 0, mMenuItemIconSize, mMenuItemIconSize);
131             mTextWithIconView.setCompoundDrawables(mMenuItem.getIcon(), null, null, null);
132             mTextWithIconView.setText(mMenuItem.getTitle());
133             clickTarget = mTextWithIconView;
134         } else if (hasIcon) {
135             mIconView.setImageDrawable(mMenuItem.getIcon());
136             clickTarget = mIconContainer;
137         } else { // hasText will be true
138             mTextView.setText(mMenuItem.getTitle());
139             clickTarget = mTextView;
140         }
141 
142         mIconContainer.setVisibility(clickTarget == mIconContainer ? View.VISIBLE : View.GONE);
143         mTextView.setVisibility(clickTarget == mTextView ? View.VISIBLE : View.GONE);
144         mTextWithIconView.setVisibility(clickTarget == mTextWithIconView
145                 ? View.VISIBLE : View.GONE);
146         mSwitch.setVisibility(clickTarget == mSwitch ? View.VISIBLE : View.GONE);
147 
148         if (!mIndividualClickListeners) {
149             clickTarget = mView;
150         }
151 
152         if (!mMenuItem.isTinted() && hasIcon) {
153             mMenuItem.getIcon().setTintList(null);
154         }
155 
156         recursiveSetEnabledAndDrawableState(mView);
157         mView.setActivated(mMenuItem.isActivated());
158 
159         if (mMenuItem.getOnClickListener() != null
160                 || mMenuItem.isCheckable()
161                 || mMenuItem.isActivatable()) {
162             clickTarget.setOnClickListener(v -> mMenuItem.performClick());
163         } else if (clickTarget == mView) {
164             mView.setOnClickListener(null);
165             mView.setClickable(false);
166         }
167     }
168 
recursiveSetEnabledAndDrawableState(View view)169     private void recursiveSetEnabledAndDrawableState(View view) {
170         view.setEnabled(mMenuItem.isEnabled());
171 
172         int[] drawableState = mMenuItem.isRestricted() ? RESTRICTED_STATE : null;
173         if (view instanceof ImageView) {
174             ((ImageView) view).setImageState(drawableState, true);
175         } else if (view instanceof DrawableStateView) {
176             ((DrawableStateView) view).setExtraDrawableState(drawableState, null);
177         }
178 
179         if (view instanceof ViewGroup) {
180             ViewGroup viewGroup = ((ViewGroup) view);
181             for (int i = 0; i < viewGroup.getChildCount(); i++) {
182                 recursiveSetEnabledAndDrawableState(viewGroup.getChildAt(i));
183             }
184         }
185     }
186 }
187