1 package com.android.car.media.widgets;
2 
3 import android.annotation.Nullable;
4 import android.annotation.NonNull;
5 import android.car.drivingstate.CarUxRestrictions;
6 import android.content.Context;
7 import android.util.AttributeSet;
8 
9 import com.android.car.media.R;
10 import com.android.car.media.common.MediaItemMetadata;
11 import com.android.car.ui.toolbar.MenuItem;
12 import com.android.car.ui.toolbar.Toolbar;
13 
14 import java.util.Arrays;
15 import java.util.List;
16 import java.util.Objects;
17 
18 /**
19  * Media template application bar. The callers should set properties via the public methods (e.g.,
20  * {@link #setItems}, {@link #setTitle}, {@link #setHasSettings}), and set the visibility of the
21  * views via {@link #setState}. A detailed explanation of all possible states of this application
22  * bar can be seen at {@link Toolbar.State}.
23  */
24 public class AppBarView extends Toolbar {
25 
26     private static final int MEDIA_UX_RESTRICTION_DEFAULT =
27             CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP;
28     private static final int MEDIA_UX_RESTRICTION_NONE = CarUxRestrictions.UX_RESTRICTIONS_BASELINE;
29 
30     private int mMaxTabs;
31 
32     @NonNull
33     private AppBarListener mListener = DEFAULT_APPBAR_LISTENER;
34     private MenuItem mSearch;
35     private MenuItem mSettings;
36     private MenuItem mEqualizer;
37     private MenuItem mAppSelector;
38 
39     private boolean mSearchSupported;
40     private boolean mShowSearchIfSupported;
41 
42     /**
43      * Application bar listener
44      */
45     public interface AppBarListener {
46         /**
47          * Invoked when the user selects an item from the tabs
48          */
onTabSelected(MediaItemMetadata item)49         void onTabSelected(MediaItemMetadata item);
50 
51         /**
52          * Invoked when the user clicks on the back button
53          */
onBack()54         void onBack();
55 
56         /**
57          * Invoked when the user clicks on the settings button.
58          */
onSettingsSelection()59         void onSettingsSelection();
60 
61         /**
62          * Invoked when the user clicks on the equalizer button.
63          */
onEqualizerSelection()64         void onEqualizerSelection();
65 
66         /**
67          * Invoked when the user submits a search query.
68          */
onSearch(String query)69         void onSearch(String query);
70 
71         /**
72          * Invoked when the user clicks on the search button
73          */
onSearchSelection()74         void onSearchSelection();
75 
76         /**
77          * Invoked when the user clicks on the app switch button
78          */
onAppSwitch()79         void onAppSwitch();
80 
81         /**
82          * Invoked when the height of the toolbar changes
83          */
onHeightChanged(int height)84         void onHeightChanged(int height);
85     }
86 
87     private static final AppBarListener DEFAULT_APPBAR_LISTENER = new AppBarListener() {
88         @Override
89         public void onTabSelected(MediaItemMetadata item) {}
90 
91         @Override
92         public void onBack() {}
93 
94         @Override
95         public void onSettingsSelection() {}
96 
97         @Override
98         public void onEqualizerSelection() {}
99 
100         @Override
101         public void onSearch(String query) {}
102 
103         @Override
104         public void onSearchSelection() {}
105 
106         @Override
107         public void onAppSwitch() {}
108 
109         @Override
110         public void onHeightChanged(int height) {}
111     };
112 
AppBarView(Context context)113     public AppBarView(Context context) {
114         this(context, null);
115     }
116 
AppBarView(Context context, AttributeSet attrs)117     public AppBarView(Context context, AttributeSet attrs) {
118         this(context, attrs, 0);
119     }
120 
AppBarView(Context context, AttributeSet attrs, int defStyleAttr)121     public AppBarView(Context context, AttributeSet attrs, int defStyleAttr) {
122         super(context, attrs, defStyleAttr);
123         init(context);
124     }
125 
init(Context context)126     private void init(Context context) {
127         mMaxTabs = context.getResources().getInteger(R.integer.max_tabs);
128 
129         registerOnTabSelectedListener(tab ->
130                 mListener.onTabSelected(((MediaItemTab) tab).getItem()));
131         registerOnBackListener(() -> {
132             mListener.onBack();
133             return true;
134         });
135         registerOnSearchListener(query -> mListener.onSearch(query));
136         registerToolbarHeightChangeListener(height -> mListener.onHeightChanged(height));
137         mSearch = MenuItem.Builder.createSearch(context, v -> mListener.onSearchSelection());
138         mSettings = new MenuItem.Builder(context)
139                 .setToSettings()
140                 .setUxRestrictions(MEDIA_UX_RESTRICTION_DEFAULT)
141                 .setOnClickListener(v -> mListener.onSettingsSelection())
142                 .build();
143         mEqualizer = new MenuItem.Builder(context)
144                 .setTitle(R.string.menu_item_sound_settings_title)
145                 .setIcon(R.drawable.ic_equalizer)
146                 .setOnClickListener(v -> mListener.onEqualizerSelection())
147                 .build();
148         mAppSelector = new MenuItem.Builder(context)
149                 .setIcon(R.drawable.ic_app_switch)
150                 .setOnClickListener(m -> mListener.onAppSwitch())
151                 .build();
152         setMenuItems(Arrays.asList(mSearch, mEqualizer, mSettings, mAppSelector));
153     }
154 
155     /**
156      * Sets a listener of this application bar events. In order to avoid memory leaks, consumers
157      * must reset this reference by setting the listener to null.
158      */
setListener(AppBarListener listener)159     public void setListener(AppBarListener listener) {
160         mListener = listener;
161     }
162 
163     /**
164      * Updates the list of items to show in the application bar tabs.
165      *
166      * @param items list of tabs to show, or null if no tabs should be shown.
167      */
setItems(@ullable List<MediaItemMetadata> items)168     public void setItems(@Nullable List<MediaItemMetadata> items) {
169         clearAllTabs();
170 
171         if (items != null && !items.isEmpty()) {
172             int count = 0;
173             for (MediaItemMetadata item : items) {
174                 addTab(new MediaItemTab(item));
175 
176                 count++;
177                 if (count >= mMaxTabs) {
178                     break;
179                 }
180             }
181         }
182     }
183 
184     /** Sets whether the source has settings (not all screens show it). */
setHasSettings(boolean hasSettings)185     public void setHasSettings(boolean hasSettings) {
186         mSettings.setVisible(hasSettings);
187     }
188 
189     /** Sets whether the source's settings is distraction optimized. */
setSettingsDistractionOptimized(boolean isDistractionOptimized)190     public void setSettingsDistractionOptimized(boolean isDistractionOptimized) {
191         mSettings.setUxRestrictions(isDistractionOptimized
192                 ? MEDIA_UX_RESTRICTION_NONE
193                 : MEDIA_UX_RESTRICTION_DEFAULT);
194     }
195 
196     /** Sets whether the source has equalizer support. */
setHasEqualizer(boolean hasEqualizer)197     public void setHasEqualizer(boolean hasEqualizer) {
198         mEqualizer.setVisible(hasEqualizer);
199     }
200 
201     /**
202      * Sets whether search is supported
203      */
setSearchSupported(boolean supported)204     public void setSearchSupported(boolean supported) {
205         mSearchSupported = supported;
206         updateSearchVisibility();
207     }
208 
209     /** Sets whether to show the search MenuItem if supported */
showSearchIfSupported(boolean show)210     public void showSearchIfSupported(boolean show) {
211         mShowSearchIfSupported = show;
212         updateSearchVisibility();
213     }
214 
updateSearchVisibility()215     private void updateSearchVisibility() {
216         mSearch.setVisible(mShowSearchIfSupported && mSearchSupported);
217     }
218 
219     /**
220      * Sets whether launching app selector is supported
221      */
setAppLauncherSupported(boolean supported)222     public void setAppLauncherSupported(boolean supported) {
223         mAppSelector.setVisible(supported);
224     }
225 
226     /**
227      * Updates the currently active item
228      */
setActiveItem(MediaItemMetadata item)229     public void setActiveItem(MediaItemMetadata item) {
230         for (int i = 0; i < getTabLayout().getTabCount(); i++) {
231             MediaItemTab mediaItemTab = (MediaItemTab) getTabLayout().get(i);
232             boolean match = item != null && Objects.equals(
233                     item.getId(),
234                     mediaItemTab.getItem().getId());
235             if (match) {
236                 getTabLayout().selectTab(i);
237                 return;
238             }
239         }
240     }
241 }
242