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