1 /* 2 * Copyright (C) 2018 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.android.car.radio; 18 19 import static com.android.car.ui.core.CarUi.requireToolbar; 20 import static com.android.car.ui.toolbar.Toolbar.State.HOME; 21 22 import android.content.Intent; 23 import android.graphics.drawable.Drawable; 24 import android.os.Bundle; 25 import android.view.KeyEvent; 26 27 import androidx.annotation.NonNull; 28 import androidx.fragment.app.FragmentActivity; 29 import androidx.viewpager.widget.ViewPager; 30 31 import com.android.car.media.common.source.MediaSource; 32 import com.android.car.media.common.source.MediaTrampolineHelper; 33 import com.android.car.radio.bands.ProgramType; 34 import com.android.car.radio.service.RadioAppService; 35 import com.android.car.radio.util.Log; 36 import com.android.car.ui.baselayout.Insets; 37 import com.android.car.ui.baselayout.InsetsChangedListener; 38 import com.android.car.ui.toolbar.MenuItem; 39 import com.android.car.ui.toolbar.TabLayout; 40 import com.android.car.ui.toolbar.ToolbarController; 41 42 import java.util.Arrays; 43 import java.util.List; 44 45 /** 46 * The main activity for the radio app. 47 */ 48 public class RadioActivity extends FragmentActivity implements InsetsChangedListener { 49 private static final String TAG = "BcRadioApp.activity"; 50 51 /** 52 * Intent action for notifying that the radio state has changed. 53 */ 54 private static final String ACTION_RADIO_APP_STATE_CHANGE = 55 "android.intent.action.RADIO_APP_STATE_CHANGE"; 56 57 /** 58 * Boolean Intent extra indicating if the radio is the currently in the foreground. 59 */ 60 private static final String EXTRA_RADIO_APP_FOREGROUND = 61 "android.intent.action.RADIO_APP_STATE"; 62 63 private RadioController mRadioController; 64 private BandController mBandController = new BandController(); 65 private ToolbarController mToolbar; 66 private RadioPagerAdapter mRadioPagerAdapter; 67 68 private boolean mUseSourceLogoForAppSelector; 69 private MediaTrampolineHelper mMediaTrampoline; 70 71 @Override onCarUiInsetsChanged(Insets insets)72 public void onCarUiInsetsChanged(Insets insets) { 73 // This InsetsChangedListener is just a marker that we will later handle 74 // insets in fragments, since the fragments aren't added immediately. 75 // Otherwise CarUi will apply the insets to the content view incorrectly. 76 } 77 78 @Override onCreate(Bundle savedInstanceState)79 protected void onCreate(Bundle savedInstanceState) { 80 super.onCreate(savedInstanceState); 81 82 Log.d(TAG, "Radio app main activity created"); 83 84 setContentView(R.layout.radio_activity); 85 86 mMediaTrampoline = new MediaTrampolineHelper(this); 87 88 mRadioController = new RadioController(this); 89 mRadioController.getCurrentProgram().observe(this, info -> { 90 ProgramType programType = ProgramType.fromSelector(info.getSelector()); 91 if (programType != null) { 92 mBandController.setType(programType); 93 updateMenuItems(); 94 } 95 }); 96 97 mRadioPagerAdapter = 98 new RadioPagerAdapter(this, getSupportFragmentManager(), mRadioController); 99 ViewPager viewPager = findViewById(R.id.viewpager); 100 viewPager.setAdapter(mRadioPagerAdapter); 101 102 mUseSourceLogoForAppSelector = 103 getResources().getBoolean(R.bool.use_media_source_logo_for_app_selector); 104 105 mToolbar = requireToolbar(this); 106 mToolbar.setState(HOME); 107 mToolbar.setTitle(R.string.app_name); 108 if (!mUseSourceLogoForAppSelector) { 109 mToolbar.setLogo(R.drawable.logo_fm_radio); 110 } 111 mToolbar.registerOnTabSelectedListener(t -> 112 viewPager.setCurrentItem(mToolbar.getTabPosition(t))); 113 114 updateMenuItems(); 115 setupTabsWithViewPager(viewPager); 116 } 117 118 @Override onStart()119 protected void onStart() { 120 super.onStart(); 121 122 mRadioController.start(); 123 124 Intent broadcast = new Intent(ACTION_RADIO_APP_STATE_CHANGE); 125 broadcast.putExtra(EXTRA_RADIO_APP_FOREGROUND, true); 126 sendBroadcast(broadcast); 127 } 128 129 @Override onNewIntent(Intent intent)130 protected void onNewIntent(Intent intent) { 131 super.onNewIntent(intent); 132 setIntent(intent); // getIntent() should always return the most recent 133 134 if (Log.isLoggable(TAG, Log.DEBUG)) { 135 Log.d(TAG, "onNewIntent: " + intent); 136 } 137 } 138 139 @Override onResume()140 protected void onResume() { 141 super.onResume(); 142 143 Intent intent = getIntent(); 144 if (Log.isLoggable(TAG, Log.DEBUG)) { 145 Log.d(TAG, "onResume intent: " + intent); 146 } 147 148 if (intent != null) { 149 mMediaTrampoline.setLaunchedMediaSource(RadioAppService.getMediaSourceComp(this)); 150 151 // Mark the intent as consumed so that coming back from the media app selector doesn't 152 // set the source again. 153 setIntent(null); 154 } 155 } 156 157 @Override onStop()158 protected void onStop() { 159 super.onStop(); 160 161 Intent broadcast = new Intent(ACTION_RADIO_APP_STATE_CHANGE); 162 broadcast.putExtra(EXTRA_RADIO_APP_FOREGROUND, false); 163 sendBroadcast(broadcast); 164 } 165 166 @Override onDestroy()167 protected void onDestroy() { 168 super.onDestroy(); 169 170 mRadioController.shutdown(); 171 172 Log.d(TAG, "Radio app main activity destroyed"); 173 } 174 175 @Override onKeyDown(int keyCode, KeyEvent event)176 public boolean onKeyDown(int keyCode, KeyEvent event) { 177 switch (keyCode) { 178 case KeyEvent.KEYCODE_MEDIA_STEP_FORWARD: 179 mRadioController.step(true); 180 return true; 181 case KeyEvent.KEYCODE_MEDIA_STEP_BACKWARD: 182 mRadioController.step(false); 183 return true; 184 default: 185 return super.onKeyDown(keyCode, event); 186 } 187 } 188 189 /** 190 * Set whether background scanning is supported, to know whether to show the browse tab or not. 191 */ setProgramListSupported(boolean supported)192 public void setProgramListSupported(boolean supported) { 193 if (supported && mRadioPagerAdapter.addBrowseTab()) { 194 updateTabs(); 195 } 196 } 197 198 /** 199 * Sets supported program types. 200 */ setSupportedProgramTypes(@onNull List<ProgramType> supported)201 public void setSupportedProgramTypes(@NonNull List<ProgramType> supported) { 202 mBandController.setSupportedProgramTypes(supported); 203 } 204 setupTabsWithViewPager(ViewPager viewPager)205 private void setupTabsWithViewPager(ViewPager viewPager) { 206 viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { 207 @Override 208 public void onPageSelected(int position) { 209 mToolbar.selectTab(position); 210 } 211 }); 212 updateTabs(); 213 } 214 updateMenuItems()215 private void updateMenuItems() { 216 ProgramType currentBand = mBandController.getCurrentBand(); 217 MenuItem bandSelectorMenuItem = MenuItem.builder(this) 218 .setIcon(currentBand != null ? currentBand.getResourceId() : 0) 219 .setOnClickListener(i -> { 220 ProgramType programType = mBandController.switchToNext(); 221 mRadioController.switchBand(programType); 222 }) 223 .build(); 224 225 Intent appSelectorIntent = MediaSource.getSourceSelectorIntent(this, false); 226 MenuItem appSelectorMenuItem = MenuItem.builder(this) 227 .setIcon(mUseSourceLogoForAppSelector 228 ? R.drawable.logo_fm_radio : R.drawable.ic_app_switch) 229 .setTinted(!mUseSourceLogoForAppSelector) 230 .setOnClickListener(m -> startActivity(appSelectorIntent)) 231 .build(); 232 233 mToolbar.setMenuItems(Arrays.asList(bandSelectorMenuItem, appSelectorMenuItem)); 234 } 235 updateTabs()236 private void updateTabs() { 237 mToolbar.clearAllTabs(); 238 for (int i = 0; i < mRadioPagerAdapter.getCount(); i++) { 239 Drawable icon = mRadioPagerAdapter.getPageIcon(i); 240 CharSequence title = mRadioPagerAdapter.getPageTitle(i); 241 mToolbar.addTab(new TabLayout.Tab(icon, title)); 242 } 243 } 244 } 245