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