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.graphics.drawable.Drawable; 7 import android.util.Size; 8 9 import androidx.annotation.NonNull; 10 import androidx.annotation.Nullable; 11 12 import com.android.car.media.MediaAppConfig; 13 import com.android.car.media.R; 14 import com.android.car.media.common.MediaItemMetadata; 15 import com.android.car.media.common.source.MediaSource; 16 import com.android.car.ui.toolbar.MenuItem; 17 import com.android.car.ui.toolbar.NavButtonMode; 18 import com.android.car.ui.toolbar.SearchCapabilities; 19 import com.android.car.ui.toolbar.SearchConfig; 20 import com.android.car.ui.toolbar.SearchMode; 21 import com.android.car.ui.toolbar.ToolbarController; 22 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.Collections; 26 import java.util.List; 27 import java.util.Objects; 28 import java.util.stream.Collectors; 29 30 /** 31 * Media template application bar. This class wraps a {@link ToolbarController} and 32 * adds media-specific methods to it like {@link #setItems} and {@link #setSearchSupported}. 33 */ 34 public class AppBarController { 35 36 private static final int MEDIA_UX_RESTRICTION_DEFAULT = 37 CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP; 38 private static final int MEDIA_UX_RESTRICTION_NONE = CarUxRestrictions.UX_RESTRICTIONS_BASELINE; 39 40 private final int mMaxTabs; 41 private final ArrayList<TabBinder<MediaItemMetadata.ArtworkRef>> mTabs = new ArrayList<>(); 42 private final ToolbarController mToolbarController; 43 private final Context mApplicationContext; 44 45 private final boolean mUseSourceLogoForAppSelector; 46 47 private final MenuItem mSearch; 48 private final MenuItem mSettings; 49 private final MenuItem mEqualizer; 50 private final MenuItem mAppSelector; 51 52 @NonNull 53 private AppBarListener mListener = new AppBarListener(); 54 private boolean mSearchSupported; 55 private boolean mShowSearchIfSupported; 56 private String mSearchQuery; 57 private int mSelectedTab = -1; 58 private Drawable mLogo; 59 60 /** 61 * Application bar listener 62 */ 63 public static class AppBarListener { 64 /** 65 * Invoked when the user selects an item from the tabs 66 */ onTabSelected(MediaItemMetadata item)67 protected void onTabSelected(MediaItemMetadata item) {} 68 69 /** 70 * Invoked when the user clicks on the settings button. 71 */ onSettingsSelection()72 protected void onSettingsSelection() {} 73 74 /** 75 * Invoked when the user clicks on the equalizer button. 76 */ onEqualizerSelection()77 protected void onEqualizerSelection() {} 78 79 /** 80 * Invoked when the user submits a search query. 81 */ onSearch(String query)82 protected void onSearch(String query) {} 83 84 /** 85 * Invoked when the user clicks on the search button 86 */ onSearchSelection()87 protected void onSearchSelection() {} 88 } 89 AppBarController(Context context, ToolbarController controller)90 public AppBarController(Context context, ToolbarController controller) { 91 mToolbarController = controller; 92 mApplicationContext = context.getApplicationContext(); 93 mMaxTabs = context.getResources().getInteger(R.integer.max_tabs); 94 95 mUseSourceLogoForAppSelector = 96 context.getResources().getBoolean(R.bool.use_media_source_logo_for_app_selector); 97 98 Intent appSelectorIntent = MediaSource.getSourceSelectorIntent(context, false); 99 100 mToolbarController.registerSearchListener(query -> { 101 mSearchQuery = query; 102 mListener.onSearch(query); 103 }); 104 mToolbarController.registerSearchCompletedListener( 105 () -> mListener.onSearch(mSearchQuery)); 106 mSearch = MenuItem.builder(context) 107 .setToSearch() 108 .setOnClickListener(v -> mListener.onSearchSelection()) 109 .build(); 110 mSettings = MenuItem.builder(context) 111 .setToSettings() 112 .setUxRestrictions(MEDIA_UX_RESTRICTION_DEFAULT) 113 .setOnClickListener(v -> mListener.onSettingsSelection()) 114 .build(); 115 mEqualizer = MenuItem.builder(context) 116 .setTitle(R.string.menu_item_sound_settings_title) 117 .setIcon(R.drawable.ic_equalizer) 118 .setOnClickListener(v -> mListener.onEqualizerSelection()) 119 .build(); 120 mAppSelector = MenuItem.builder(context) 121 .setTitle(R.string.menu_item_app_selector_title) 122 .setTinted(!mUseSourceLogoForAppSelector) 123 .setIcon(mUseSourceLogoForAppSelector 124 ? null : context.getDrawable(R.drawable.ic_app_switch)) 125 .setOnClickListener(m -> context.startActivity(appSelectorIntent)) 126 .build(); 127 mToolbarController.setMenuItems( 128 Arrays.asList(mSearch, mEqualizer, mSettings, mAppSelector)); 129 130 mAppSelector.setVisible(appSelectorIntent != null); 131 } 132 133 /** 134 * Sets a listener of this application bar events. In order to avoid memory leaks, consumers 135 * must reset this reference by setting the listener to null. 136 */ setListener(@onNull AppBarListener listener)137 public void setListener(@NonNull AppBarListener listener) { 138 mListener = listener; 139 } 140 141 /** 142 * Updates the list of items to show in the application bar tabs. 143 * 144 * @param items list of tabs to show, or null if no tabs should be shown. 145 */ setItems(@ullable List<MediaItemMetadata> items)146 public void setItems(@Nullable List<MediaItemMetadata> items) { 147 if (items == null) { 148 items = Collections.emptyList(); 149 } 150 151 for (TabBinder<MediaItemMetadata.ArtworkRef> tabBinder : mTabs) { 152 tabBinder.setUpdateListener(null); 153 tabBinder.setImage(mApplicationContext, null); 154 } 155 156 mTabs.clear(); 157 158 Size maxArtSize = MediaAppConfig.getMediaItemsBitmapMaxSize(mApplicationContext); 159 for (MediaItemMetadata item : items.subList(0, Math.min(items.size(), mMaxTabs))) { 160 TabBinder<MediaItemMetadata.ArtworkRef> newTab = new TabBinder<>( 161 mApplicationContext, 162 maxArtSize, 163 item, 164 item2 -> { 165 mSelectedTab = mTabs.indexOf(item2); 166 mListener.onTabSelected(item2.getMediaItemMetadata()); 167 }); 168 newTab.setImage(mApplicationContext, item.getArtworkKey()); 169 mTabs.add(newTab); 170 } 171 mSelectedTab = mTabs.isEmpty() ? -1 : 0; 172 for (TabBinder<MediaItemMetadata.ArtworkRef> tabBinder : mTabs) { 173 tabBinder.setUpdateListener(x -> updateTabs()); 174 } 175 updateTabs(); 176 } 177 updateTabs()178 private void updateTabs() { 179 if (mToolbarController.getNavButtonMode() != NavButtonMode.DISABLED) { 180 mToolbarController.setTabs(Collections.emptyList()); 181 } else { 182 mToolbarController.setTabs(mTabs.stream() 183 .map(TabBinder::getToolbarTab) 184 .collect(Collectors.toList()), 185 mSelectedTab); 186 } 187 } 188 189 /** Sets whether the source has settings (not all screens show it). */ setHasSettings(boolean hasSettings)190 public void setHasSettings(boolean hasSettings) { 191 mSettings.setVisible(hasSettings); 192 } 193 194 /** Sets whether the source's settings is distraction optimized. */ setSettingsDistractionOptimized(boolean isDistractionOptimized)195 public void setSettingsDistractionOptimized(boolean isDistractionOptimized) { 196 mSettings.setUxRestrictions(isDistractionOptimized 197 ? MEDIA_UX_RESTRICTION_NONE 198 : MEDIA_UX_RESTRICTION_DEFAULT); 199 } 200 201 /** Sets whether the source has equalizer support. */ setHasEqualizer(boolean hasEqualizer)202 public void setHasEqualizer(boolean hasEqualizer) { 203 mEqualizer.setVisible(hasEqualizer); 204 } 205 206 /** 207 * Sets whether search is supported 208 */ setSearchSupported(boolean supported)209 public void setSearchSupported(boolean supported) { 210 mSearchSupported = supported; 211 updateSearchVisibility(); 212 } 213 214 /** Sets whether to show the search MenuItem if supported */ showSearchIfSupported(boolean show)215 public void showSearchIfSupported(boolean show) { 216 mShowSearchIfSupported = show; 217 updateSearchVisibility(); 218 } 219 updateSearchVisibility()220 private void updateSearchVisibility() { 221 mSearch.setVisible(mShowSearchIfSupported && mSearchSupported); 222 } 223 224 /** 225 * Updates the currently active item 226 */ setActiveItem(MediaItemMetadata item)227 public void setActiveItem(MediaItemMetadata item) { 228 for (int i = 0; i < mTabs.size(); i++) { 229 MediaItemMetadata mediaItemMetadata = mTabs.get(i).getMediaItemMetadata(); 230 boolean match = item != null && Objects.equals( 231 item.getId(), 232 mediaItemMetadata.getId()); 233 if (match) { 234 mToolbarController.selectTab(i); 235 return; 236 } 237 } 238 } 239 setSearchQuery(String query)240 public void setSearchQuery(String query) { 241 mToolbarController.setSearchQuery(query); 242 } 243 setLogo(Drawable drawable)244 public void setLogo(Drawable drawable) { 245 mLogo = drawable; 246 updateLogo(); 247 } 248 updateLogo()249 private void updateLogo() { 250 if (mToolbarController.getSearchMode() == SearchMode.DISABLED) { 251 if (mUseSourceLogoForAppSelector) { 252 mAppSelector.setIcon(mLogo); 253 } else { 254 mToolbarController.setLogo(mLogo); 255 } 256 } else { 257 mToolbarController.setLogo(null); 258 } 259 } 260 setSearchIcon(Drawable drawable)261 public void setSearchIcon(Drawable drawable) { 262 mToolbarController.setSearchIcon(drawable); 263 } 264 setTitle(CharSequence title)265 public void setTitle(CharSequence title) { 266 mToolbarController.setTitle(title); 267 } 268 setTitle(int title)269 public void setTitle(int title) { 270 mToolbarController.setTitle(title); 271 } 272 setMenuItems(List<MenuItem> items)273 public void setMenuItems(List<MenuItem> items) { 274 mToolbarController.setMenuItems(items); 275 } 276 setBackgroundShown(boolean shown)277 public void setBackgroundShown(boolean shown) { 278 mToolbarController.setBackgroundShown(shown); 279 } 280 281 /** Proxies to {@link ToolbarController#setSearchMode(SearchMode)} */ setSearchMode(SearchMode mode)282 public void setSearchMode(SearchMode mode) { 283 if (mToolbarController.getSearchMode() != mode) { 284 mToolbarController.setSearchMode(mode); 285 updateTabs(); 286 updateLogo(); 287 } 288 } 289 290 /** Proxies to {@link ToolbarController#setNavButtonMode(NavButtonMode)} */ setNavButtonMode(NavButtonMode mode)291 public void setNavButtonMode(NavButtonMode mode) { 292 if (mode != mToolbarController.getNavButtonMode()) { 293 mToolbarController.setNavButtonMode(mode); 294 updateTabs(); 295 } 296 } 297 298 /** Proxies to {@link ToolbarController#getSearchCapabilities()} */ getSearchCapabilities()299 public SearchCapabilities getSearchCapabilities() { 300 return mToolbarController.getSearchCapabilities(); 301 } 302 303 /** Proxies to {@link ToolbarController#setSearchConfig(SearchConfig)} */ setSearchConfig(SearchConfig searchConfig)304 public void setSearchConfig(SearchConfig searchConfig) { 305 mToolbarController.setSearchConfig(searchConfig); 306 } 307 } 308